{*******************************************************************}
{                                                                   }
{       Developer Express Visual Component Library                  }
{       ExpressPrinting System COMPONENT SUITE                      }
{                                                                   }
{       Copyright (C) 1998-2014 Developer Express Inc.              }
{       ALL RIGHTS RESERVED                                         }
{                                                                   }
{   The entire contents of this file is protected by U.S. and       }
{   International Copyright Laws. Unauthorized reproduction,        }
{   reverse-engineering, and distribution of all or any portion of  }
{   the code contained in this file is strictly prohibited and may  }
{   result in severe civil and criminal penalties and will be       }
{   prosecuted to the maximum extent possible under the law.        }
{                                                                   }
{   RESTRICTIONS                                                    }
{                                                                   }
{   THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES           }
{   (DCU, OBJ, DLL, ETC.) ARE CONFIDENTIAL AND PROPRIETARY TRADE    }
{   SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS   }
{   LICENSED TO DISTRIBUTE THE EXPRESSPRINTINGSYSTEM AND            }
{   ALL ACCOMPANYING VCL CONTROLS AS PART OF AN                     }
{   EXECUTABLE PROGRAM ONLY.                                        }
{                                                                   }
{   THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED      }
{   FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE        }
{   COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE       }
{   AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT  }
{   AND PERMISSION FROM DEVELOPER EXPRESS INC.                      }
{                                                                   }
{   CONSULT THE END USER LICENSE AGREEMENT FOR INFORMATION ON       }
{   ADDITIONAL RESTRICTIONS.                                        }
{                                                                   }
{*******************************************************************}

unit dxPScxSSLnk;

interface

{$I cxVer.inc}

uses
  Types, Windows, Classes, Graphics, Controls, StdCtrls, ExtCtrls, ComCtrls, Dialogs,
  cxSSTypes, cxSSData, cxSSStyles, cxSSheet, cxExcelConst, dxPSCore, dxPSForm, 
  dxPSGlbl, dxPSBaseGridLnk, dxPSExcelEdgePatterns, dxPSExcelFillPatterns,
  dxPSEdgePatterns, dxPSFillPatterns, cxDrawTextUtils, cxPC, cxControls,
  cxContainer, cxEdit, cxLabel, cxCheckBox, Menus, cxLookAndFeelPainters,
  cxTextEdit, cxButtons, cxGraphics, cxMaskEdit, cxDropDownEdit,
  cxColorComboBox, dxPSReportRenderCanvas, cxLookAndFeels;

type
  PdxPSCellBorder = ^TdxPSCellBorder;
  TdxPSCellBorder = record
    Color: TColor;
    Pattern: TdxPSEdgePatternClass;
  end;

  PdxPSCellBorders = ^TdxPSCellBorders;
  TdxPSCellBorders = array[TdxCellSide] of TdxPSCellBorder;

  TdxPSCellBorderCorner = (cbcTopLeft, cbcTopRight, cbcBottomRight, cbcBottomLeft);
  TdxPSCellBorderCorners = set of TdxPSCellBorderCorner;
  
  TdxPSCellBorderEnd = (cbsTopLeft, cbsBottomRight);
  TdxPSCellBorderEnds = set of TdxPSCellBorderEnd;

  TdxPSCellBorderSub = 0..3;
  
  TdxPSCellCorner = (ccTopLeft, ccTopRight, ccBottomRight, ccBottomLeft);

  TdxPSCellPatternsBorderPainter = class;
  TdxPSGridCellsAdapter = class;

  TdxReportCellSSString = class(TdxReportCellString)
  private
    FBorders: TdxPSCellBorders;
    FBorderSlants: DWORD;
    FBorderSubs: DWORD;
    FContentBkColor: TColor;
    FContentPattern: TdxPSFillPatternClass;
    FRealCol: Integer;
    FRealRow: Integer;
    FTextExtentLeft: Integer;
    FTextExtentRight: Integer;
    function GetBorder(ASide: TdxCellSide): TdxPSCellBorder;
    function GetBordersBkColor: TColor;
    function GetBorderSlant(ASide: TdxCellSide; ACorner: TdxPSCellBorderCorner): Integer;
    function GetBorderSlantOffset(ASide: TdxCellSide; ACorner: TdxPSCellBorderCorner): TDWORDBits;
    function GetBorderSub(ASide: TdxCellSide; AEnd: TdxPSCellBorderEnd): TdxPSCellBorderSub;
    function GetBorderSubMask(ASide: TdxCellSide; AEnd: TdxPSCellBorderEnd): DWORD;
    function GetBorderSubOffset(ASide: TdxCellSide; AEnd: TdxPSCellBorderEnd): TDWORDBits;
    function GetClipContent: Boolean;
    function GetCol: Integer;
    function GetFill: Boolean;
    function GetIsFixed: Boolean;  
    function GetIsMerged: Boolean;
    function GetIsNearMostLeft: Boolean;
    function GetIsNearMostTop: Boolean;
    function GetIsNearMostTopOrLeft: Boolean;
    function GetIsVirtual: Boolean;
    function GetRow: Integer;    
    procedure SetBorder(ASide: TdxCellSide; Value: TdxPSCellBorder);
    procedure SetBorderSlant(ASide: TdxCellSide; ACorner: TdxPSCellBorderCorner; Value: Integer);
    procedure SetBorderSub(ASide: TdxCellSide; AEnd: TdxPSCellBorderEnd; Value: TdxPSCellBorderSub);
    procedure SetClipConent(Value: Boolean);
    procedure SetFill(Value: Boolean);
    procedure SetIsFixed(Value: Boolean);
    procedure SetIsMerged(Value: Boolean);
    procedure SetIsNearMostLeft(Value: Boolean);
    procedure SetIsNearMostTop(Value: Boolean);
    procedure SetIsVirtual(Value: Boolean);
  protected
    procedure CalcBorderSubs(AnAdapter: TdxPSGridCellsAdapter);
    procedure CalcDoubleBorderSlants(AnAdapter: TdxPSGridCellsAdapter);
    
    procedure ConvertCoords(APixelsNumerator, APixelsDenominator: Integer); override;
    function GetBackgroundBounds(ACanvas: TdxPSReportRenderCustomCanvas): TRect; override;
    function GetBorderEdgeBounds(ASide: TdxCellSide; const AOuterRect: TRect): TRect; override;
    function GetBorderBrush(ASide: TdxCellSide): TBrush;
    function GetBorderEdgeClass(ASide: TdxCellSide): TdxPSCellBorderClass; override;
    function GetEffectiveBounds(ACanvas: TdxPSReportRenderCustomCanvas;
      AStage: TdxPSRenderStages): TRect; override;
    function GetTextBounds(ACanvas: TdxPSReportRenderCustomCanvas): TRect; override;
    function IsDrawn(ACanvas: TdxPSReportRenderCustomCanvas;
      AStage: TdxPSRenderStages; const ARect: TRect): Boolean; override;
    
    function GetBorderPainterClass: TdxPSCellBorderPainterClass; override;
    procedure InitBorderPainter(ABordersPainter: TdxPSCellBorderPainter); override;
    
    function GetContentBkColor: TColor; override;    
    function GetContentPattern: TdxPSFillPatternClass; override;
    procedure SetContentBkColor(Value: TColor); override;        
    procedure SetContentPattern(Value: TdxPSFillPatternClass); override;

    procedure ReadBorders(AReader: TdxPSDataReader);
    procedure ReadData(AReader: TdxPSDataReader); override;
    procedure WriteBorders(AWriter: TdxPSDataWriter);
    procedure WriteData(AWriter: TdxPSDataWriter); override;
    
    function GetAbsoluteInnerBounds(ACanvas: TdxPSReportRenderCustomCanvas): TRect;
    function GetDoubleBorderRgn(ASide: TdxCellSide; const R: TRect): HRGN;
    function IsDoubleLineBorderPattern(ABorder: TdxPSCellBorder): Boolean; overload;
    function IsDoubleLineBorderPattern(ASide: TdxCellSide): Boolean; overload;
    function NullBorder: TdxPSCellBorder;
  public
    constructor Create(AParent: TdxReportCell); override;
    procedure Assign(Source: TPersistent); override;
     
    procedure SetBorders(AColor: TColor; APattern: TdxPSEdgePatternClass);

    procedure DrawContent(ACanvas: TdxPSReportRenderCustomCanvas; AStage: TdxPSRenderStages); override;

    property Borders[ASide: TdxCellSide]: TdxPSCellBorder read GetBorder write SetBorder;
    property BordersBkColor: TColor read GetBordersBkColor;
    property BorderSlants[ASide: TdxCellSide; ACorner: TdxPSCellBorderCorner]: Integer read GetBorderSlant write SetBorderSlant;
    property BorderSubs[ASide: TdxCellSide; AEnd: TdxPSCellBorderEnd]: TdxPSCellBorderSub read GetBorderSub write SetBorderSub;
    property ClipContent: Boolean read GetClipContent write SetClipConent;
    property Col: Integer read GetCol;
    property Fill: Boolean read GetFill write SetFill;
    property IsFixed: Boolean read GetIsFixed write SetIsFixed;
    property IsMerged: Boolean read GetIsMerged write SetIsMerged;
    property IsNearMostLeft: Boolean read GetIsNearMostLeft write SetIsNearMostLeft;
    property IsNearMostTop: Boolean read GetIsNearMostTop write SetIsNearMostTop;
    property IsNearMostTopOrLeft: Boolean read GetIsNearMostTopOrLeft;
    property IsVirtual: Boolean read GetIsVirtual write SetIsVirtual;
    property RealCol: Integer read FRealCol write FRealCol;
    property RealRow: Integer read FRealRow write FRealRow;
    property Row: Integer read GetRow;
    property TextExtentLeft: Integer read FTextExtentLeft write FTextExtentLeft;
    property TextExtentRight: Integer read FTextExtentRight write FTextExtentRight;
  end;

  TdxPSCellPatternsBorderPainter = class(TdxPSCellBorderPainter)
  private
    FGridAdapter: TdxPSGridCellsAdapter;
  protected
    procedure ClipItemBounds(ACanvas: TdxPSReportRenderCustomCanvas; AOuterRect: TRect);
    procedure DrawBorder(ACanvas: TdxPSReportRenderCustomCanvas;
      const R: TRect; ASide: TdxCellSide);
  public
    function Item: TdxReportCellSSString; reintroduce; overload;
    procedure DrawBorders(ACanvas: TdxPSReportRenderCustomCanvas; const R: TRect); override;
    //
    property GridAdapter: TdxPSGridCellsAdapter read FGridAdapter;
  end;

  TdxPSGridCellsAdapter = class
  private
    FReportCells: TdxReportCells;
    function GetCell(Col, Row: Integer): TdxReportCellSSString;
    function GetColCount: Integer;
    function GetColOffset(Index: Integer): Integer;    
    function GetColWidth(Index: Integer): Integer;
    function GetRow(Index: Integer): TdxReportCell;
    function GetRowCount: Integer;
    function GetRowHeight(Index: Integer): Integer;    
    function GetRowIndex(Index: Integer): Integer;
    function GetRowOffset(Index: Integer): Integer;        
  public
    constructor Create(AReportCells: TdxReportCells);
    function GetNeighborCell(AItem: TdxReportCellSSString; ASide: TdxCellSide): TdxReportCellSSString;
    
    property Cells[Col, Row: Integer]: TdxReportCellSSString read GetCell; default;
    property ColCount: Integer read GetColCount;
    property ColOffsets[Index: Integer]: Integer read GetColOffset;
    property ColWidths[Index: Integer]: Integer read GetColWidth;
    property RowCount: Integer read GetRowCount;
    property RowHeights[Index: Integer]: Integer read GetRowHeight;
    property RowIndexes[Index: Integer]: Integer read GetRowIndex;
    property RowOffsets[Index: Integer]: Integer read GetRowOffset;
    property Rows[Index: Integer]: TdxReportCell read GetRow;
  end;

  TdxPSSSStringGridCellDataMap = class(TdxPSTextGridCellDataMap)
  protected
    class procedure InitializeCellData(ACol, ARow: Integer; ADataItem: TAbstractdxReportCellData; 
      AReportLink: TAbstractdxGridReportLink); override;
    class function DataClass: TdxReportCellDataClass; override;
  end;
  
  TdxPSSpreadSheetReportLinkOptionView = (ssovRowAndColumnHeadings, ssovGridLines, ssovSuppressSourceFormats);
  TdxPSSpreadSheetReportLinkOptionsView = set of TdxPSSpreadSheetReportLinkOptionView;
  
  TCustomdxSpreadSheetReportLink = class(TAbstractdxGridReportLink)
  private
    FAppendingExtraColumns: Boolean;
    FColCount: Integer;  
    FCellObjects: TList;
    FExtraColumnCount: Integer;
    FGridAdapter: TdxPSGridCellsAdapter;
    FOptionsView: TdxPSSpreadSheetReportLinkOptionsView;
    FPrintArea: TRect;
    FProcessingMerges: Boolean;
    FRowCount: Integer;
    FTempFont: TFont;
    function GetBookSheet: TcxSSBookSheet;
    function GetCustomSpreadSheetBook: TcxCustomSpreadSheetBook;
    function GetMeaningColCount: Integer;
    function GetMeaningRowCount: Integer;    
    function GetMergedCells: TcxSSMergedCellsStorage;
    procedure SetOptionsView(Value: TdxPSSpreadSheetReportLinkOptionsView);
    procedure SetPrintArea(Value: TRect);
    function GetSSCellObject(ACol, ARow: Integer): TcxSSCellObject;
    function GetSSCellStyle(ACol, ARow: Integer): TcxSSCellStyle;
    function GetSSColorPalette: PcxExcelPalette;
    function GetSSDataStorage: TcxSSDataStorage;
    function GetSSDefaultCellSideColor(ASide: TdxCellSide): TColor;
    function GetSSDefaultStyle: TcxSSCellStyle;
    function GetFlatIndex(ACol, ARow: Integer): Integer;
    function GetRealColor(AColorIndex: Integer; ADefaultColor: TColor): TColor;
    function IsSSDefaultStyle(AStyle: TcxSSCellStyle): Boolean;
  protected
    FSourceHeaderFontIndex: Integer;
    
    function CannotActivateReportErrorString: string; override;
    procedure DoChangeComponent; override;
    procedure InternalRestoreDefaults; override;
    procedure InternalRestoreFromOriginal; override;
    function IsScaleGridLines: Boolean; override;
    function NeedTwoPassRendering: Boolean; override;

    function GetDataItemClass(ACol: Integer; ARow: Integer = 0): TdxReportCellDataClass; override;

    function GetColCount: Integer; override;
    function GetFixedColCount: Integer; override;
    function GetFixedRowCount: Integer; override;
    function GetRowCount: Integer; override;
        
    function GetCellSides(ACol, ARow: Integer): TdxCellSides; override;
    function GetCellText(ACol, ARow: Integer): string; override;
    function GetCellTextAlignY(ACol, ARow: Integer): TcxTextAlignY; override;
    function GetMinRowHeight(ACanvas: TdxPSReportRenderCustomCanvas; AFont: TFont): Integer; override;
    function GetSelectionRect: TRect; override;
    function GetSourceCellColor(ACol, ARow: Integer): TColor; override;      
    function GetSourceCellContentBkColor(ACol, ARow: Integer): TColor; override;
    function GetSourceCellContentPattern(ACol, ARow: Integer): TdxPSFillPatternClass; override;
    function GetSourceCellEdge3DSoft(ACol, ARow: Integer): Boolean; override;
    function GetSourceCellEdgeMode(ACol, ARow: Integer): TdxCellEdgeMode; override;    
    function GetSourceCellFontIndex(ACol, ARow: Integer): Integer; override;
    function GetSourceCellMultiline(ACol, ARow: Integer): Boolean; override;
    function GetSourceCellTextAlignX(ACol, ARow: Integer): TcxTextAlignX; override;
    function GetSourceCellTextAlignY(ACol, ARow: Integer): TcxTextAlignY; override;
    function GetSourceCellTransparent(ACol, ARow: Integer): Boolean; override;
    function GetSourceColWidth(ACol: Integer): Integer; override;
    function GetSourceRowHeight(ARow: Integer): Integer; override;
    function HasSelection: Boolean; override;
    function IsDrawBorder: Boolean; override;
    function IsDrawHorzLines: Boolean; override;
    function IsDrawVertLines: Boolean; override;
    function IsEmptyRow(ARow: Integer): Boolean; override;
    function IsProcessedCol(ACol: Integer): Boolean; override;
    function IsProcessedRow(ARow: Integer): Boolean; override;

    procedure AfterConstruct(AReportCells: TdxReportCells); override;
    procedure PrepareConstruct(AReportCells: TdxReportCells); override;
    procedure UnprepareConstruct(AReportCells: TdxReportCells); override;
    
    procedure AddMerges(AReportCells: TdxReportCells);
    procedure AppendAdditionalColumns(AnAdapter: TdxPSGridCellsAdapter);
    procedure CalcTextExtents(AnAdapter: TdxPSGridCellsAdapter);
    procedure DeleteCellObjects;
    procedure DeleteUnneededCellSides(AnAdapter: TdxPSGridCellsAdapter);
    procedure FixupRowWidths(AnAdapter: TdxPSGridCellsAdapter);
    function HasMerges: Boolean;
    function IsEmptyCell(const ACell: TcxSSCellRec): Boolean;
    function OnlyEdgeIsAssigned(const ACell: TcxSSCellRec; AEdge: TcxSSEdgeBorder): Boolean;
    function OnlyLeftEdgeIsAssigned(ACell: TcxSSCellRec): Boolean;
    function OnlyTopEdgeIsAssigned(ACell: TcxSSCellRec): Boolean;
    procedure PostProcessItems(AnAdapter: TdxPSGridCellsAdapter);    
    procedure SetupCellBorders(ACol, ARow: Integer; ADataItem: TAbstractdxReportCellData); virtual;
    
    procedure CheckPrintAreaBounds(var R: TRect);
    function GetColumnOffset(ACol: Integer): Integer;
    function GetRowOffset(ARow: Integer): Integer;
    function IsMergedBorder(ACol, ARow: Integer; ASide: TdxCellSide): Boolean;
    function IsMergedCell(ACol, ARow: Integer): Boolean; virtual;
    function IsNearMostLeftCell(ACol, ARow: Integer): Boolean; virtual;
    function IsNearMostTopCell(ACol, ARow: Integer): Boolean; virtual;
    function IsShowGridLines: Boolean;
    function IsShowRowAndColumnHeadings: Boolean;
    function IsSuppressSourceFormats: Boolean;    
    
    property ColCount: Integer read GetColCount;
    property ExtraColumnCount: Integer read FExtraColumnCount;
    property MeaningColCount: Integer read GetMeaningColCount;
    property MeaningRowCount: Integer read GetMeaningRowCount;
    property MergedCells: TcxSSMergedCellsStorage read GetMergedCells;
    property RowCount: Integer read GetRowCount;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
    
    procedure ClearPrintArea;
    function DataProviderPresent: Boolean; override;  
    function DataToPrintExist: Boolean; virtual;
    function PrintAreaExists: Boolean; virtual;
    
    property BookSheet: TcxSSBookSheet read GetBookSheet;
    property CustomSpreadSheetBook: TcxCustomSpreadSheetBook read GetCustomSpreadSheetBook;
    property PrintArea: TRect read FPrintArea write SetPrintArea;
  published
    property Color;
    property Effects3D;
    property FixedColor;
    property FixedFont;
    property FixedTransparent;
    property Font;
    property GridLineColor;
    property HeadersOnEveryPage;
    property OnlySelected;
    property OptionsView: TdxPSSpreadSheetReportLinkOptionsView read FOptionsView write SetOptionsView 
      default [ssovRowAndColumnHeadings];
    property RowAutoHeight;
    property ScaleFonts;
    property Soft3D;
    property Transparent;
    property UseCustomPageBreaks;
    property UseHorzDelimiters;
    property UseVertDelimiters;
    
    property OnGetCustomPageBreaks;
    property OnInitializeItem;
  end;
  
  TdxSpreadSheetBookReportLink = class(TCustomdxSpreadSheetReportLink)
  private
    function GetSpreadSheetBook: TcxSpreadSheetBook;
  public  
    property SpreadSheetBook: TcxSpreadSheetBook read GetSpreadSheetBook;
  end;

  TdxSpreadSheetReportLink = class(TCustomdxSpreadSheetReportLink)
  private
    function GetSpreadSheet: TcxSpreadSheet;
  public  
    property SpreadSheet: TcxSpreadSheet read GetSpreadSheet;
  end;

  TfmdxSpreadSheetDesignWindow = class(TStandarddxReportLinkDesignWindow)
    pnlPreview: TPanel;
    pgctrlMain: TcxPageControl;
    tshOptions: TcxTabSheet;
    Image5: TImage;
    imgGrid: TImage;
    tshColor: TcxTabSheet;
    tshFont: TcxTabSheet;
    tshBehaviors: TcxTabSheet;
    Image3: TImage;
    Image1: TImage;
    lblPreview: TcxLabel;
    stTransparent: TcxLabel;
    stFixedTransparent: TcxLabel;
    lblShow: TcxLabel;
    lblMiscellaneous: TcxLabel;
    lblGridLinesColor: TcxLabel;
    lblOnEveryPage: TcxLabel;
    lblSelection: TcxLabel;
    chbxRowAutoHeight: TcxCheckBox;
    chbxShowRowAndColumnHeadings: TcxCheckBox;
    chbxShowGridlines: TcxCheckBox;
    chbxSuppressSourceFormats: TcxCheckBox;
    chbxTransparent: TcxCheckBox;
    chbxFixedTransparent: TcxCheckBox;
    chbxOnlySelected: TcxCheckBox;
    chbxFixedRowsOnEveryPage: TcxCheckBox;
    btnFont: TcxButton;
    btnFixedFont: TcxButton;
    edFont: TcxTextEdit;
    edFixedFont: TcxTextEdit;
    ccbxLineColor: TcxColorComboBox;
    lblColor: TcxLabel;
    ccbxColor: TcxColorComboBox;
    ccbxFixedColor: TcxColorComboBox;
    lblFixedColor: TcxLabel;
    cxLabel1: TcxLabel;
    procedure chbxFixedRowsOnEveryPageClick(Sender: TObject);
    procedure chbxOnlySelectedClick(Sender: TObject);
    procedure btnFixedFontClick(Sender: TObject);
    procedure chbxRowAutoHeightClick(Sender: TObject);
    procedure chbxOptionsViewChanged(Sender: TObject);
    procedure chbxFixedTransparentClick(Sender: TObject);
    procedure ccbxColorChange(Sender: TObject);
    procedure pbxPreviewPaint(Sender: TObject);
    procedure lblColorClick(Sender: TObject);
    procedure pgctrlMainChange(Sender: TObject);
    procedure stTransparentClick(Sender: TObject);
    procedure stFixedTransparentClick(Sender: TObject);
  private
    FPreviewBox: TCustomControl;
    function GetReportLink: TCustomdxSpreadSheetReportLink;
    procedure CreateControls;
    procedure SetReportLink(Value: TCustomdxSpreadSheetReportLink);
    procedure CMDialogChar(var Message: TCMDialogChar); message CM_DIALOGCHAR;
  protected
    procedure DoInitialize; override;
    function GetPreviewHost: TCustomPanel; override;
    procedure LoadGroupsIcons; override;
    procedure LoadStrings; override;
    procedure PaintPreview(ACanvas: TCanvas; R: TRect); override;
    procedure UpdateControlsState; override;
    procedure UpdatePreview; override;
  public
    constructor Create(AOwner: TComponent); override;   
    property ReportLink: TCustomdxSpreadSheetReportLink read GetReportLink write SetReportLink;
  end;
  
const
  dxPSEmptySSPrintArea: TRect = (Left: -1; Top: -1; Right: -1; Bottom: -1);

  dxPSCellSideMap: array[TcxSSEdgeBorder] of TdxCellSide = (csLeft, csTop, csRight, csBottom);
  cxSSEdgeBorderMap: array[TdxCellSide] of TcxSSEdgeBorder = (eLeft, eTop, eRight, eBottom);

  dxCellEdgeSideOrientation: array[TdxCellSide] of TdxPSCellEdgePatternOrientation = 
    (cepoVertical, cepoHorizontal, cepoVertical, cepoHorizontal);

  dxPSEdgePatternClassMap: array[TcxSSEdgeLineStyle] of TdxPSEdgePatternClass = 
   (TdxPSSolidEdgePattern, TdxPSSolidEdgePattern, TdxPSMediumSolidEdgePattern,
    TdxPSDashedEdgePattern, TdxPSDottedEdgePattern, TdxPSThickSolidEdgePattern,
    TdxPSDoubleLineSolidEdgePattern, TdxPSHairEdgePattern, TdxPSMediumDashedEdgePattern, 
    TdxPSDashDotEdgePattern, TdxPSMediumDashDotEdgePattern, TdxPSDashDotDotEdgePattern, 
    TdxPSMediumDashDotDotEdgePattern, TdxPSSlantedDashDotEdgePattern, TdxPSSolidEdgePattern);

  dxPSFillPatternClassMap: array[TcxSSFillStyle] of TdxPSFillPatternClass = 
   (TdxPSSolidFillPattern, TdxPSGray75FillPattern, TdxPSGray50FillPattern, 
    TdxPSGray25FillPattern, TdxPSGray125FillPattern, TdxPSGray625FillPattern,
    TdxPSHorizontalStripeFillPattern, TdxPSVerticalStripeFillPattern, 
    TdxPSDiagonalStripeFillPattern, TdxPSReverseDiagonalStripeFillPattern,
    TdxPSDiagonalCrossHatchFillPattern, TdxPSThickCrossHatchFillPattern,
    TdxPSThinHorizontalStripeFillPattern, TdxPSThinVerticalStripeFillPattern, 
    TdxPSThinDiagonalStripeFillPattern, TdxPSThinReverseDiagonalStripeFillPattern,
    TdxPSThinHorizontalCrossHatchFillPattern, TdxPSThinDiagonalCrossHatchFillPattern);
    
  dxPSTextAlignXMap: array[TcxDisplayTextAlignment] of TcxTextAlignX =
    (taLeft, taCenterX, taRight, taLeft, taDistributeX);
  dxPSTextAlignYMap: array[TcxVertTextAlign] of TcxTextAlignY =
    (taTop, taCenterY, taBottom, taDistributeY);
  
implementation

{$R *.DFM}

uses                             
  SysUtils, Forms, Math,
  cxSSUtils, cxClasses, dxPSRes, dxPSUtl, dxExtCtrls, dxPrnDev, dxPSImgs;

const
  { Since we don't use following Format Bits in TdxReportCellSSString we are allowed to safely override them }
  dxFormatClipContent  = dxPSGlbl.dxFormatMakeSpaceForEmptyImage;
  dxFormatFill         = dxPSGlbl.dxFormatCheckEnabled;
  dxFormatFixed        = dxPSGlbl.dxFormatCheckBold;
  dxFormatMerged       = dxPSGlbl.dxFormatImageTransparent;
  dxFormatNearMostLeft = dxPSGlbl.dxFormatCheckFlat;
  dxFormatNearMostTop  = dxPSGlbl.dxFormatCheckChecked;
  dxFormatVirtual      = dxPSGlbl.dxFormatImageTransparent;
  
  SubEndMask = $00000003;
  SubBitsPerEnd = 2;
  SubBitsPerSide = SubBitsPerEnd * (Integer(High(TdxPSCellBorderEnd)) + 1);
  SlantBitsPerSide = 4;
  SlantMask = $00000001;

  { from SS }
  SystemColorStart = 55;

type
  TcxCustomSpreadSheetBookAccess = class(TcxCustomSpreadSheetBook);
  TcxSSBookSheetAccess = class(TcxSSBookSheet);
  TcxSSDataStorageAccess = class(TcxSSDataStorage);
  TcxSSCellStyleAccess = class(TcxSSCellStyle);
    
function AreBackgroundsEqual(AStyle1, AStyle2: TcxSSCellStyleRec): Boolean;
begin
  Result := 
    (AStyle1.BrushStyle = AStyle2.BrushStyle) and
    (AStyle1.BrushBkColor = AStyle2.BrushBkColor) and
    (AStyle1.BrushFgColor = AStyle2.BrushFgColor);
end;

function AreEdgesEqual(AEdge1, AEdge2: TcxSSEdgeStyleRec): Boolean;
begin
  Result := (AEdge1.Color = AEdge2.Color) and (AEdge1.Style = AEdge2.Style);
end;

function AreBordersEqual(ABorders1, ABorders2: TcxSSBordersStyle; 
  AExclusion: TcxSSEdgeBorders = []): Boolean;
var
  Edge: TcxSSEdgeBorder;
begin
  for Edge := eLeft to eBottom do 
  begin
    if Edge in AExclusion then
      Result := not AreEdgesEqual(ABorders1[Edge], ABorders2[Edge])
    else
      Result := AreEdgesEqual(ABorders1[Edge], ABorders2[Edge]);
    if not Result then Break;
  end;    
end;

function AreStylesEqual(AStyle1, AStyle2: TcxSSCellStyleRec; 
  AExclusion: TcxSSEdgeBorders = []): Boolean;
begin
  Result := AreBackgroundsEqual(AStyle1, AStyle2) and 
    AreBordersEqual(AStyle1.Borders, AStyle2.Borders, AExclusion);
end;

function IsCellDataEmpty(const ACell: TcxSSCellRec): Boolean;
begin
  Result := (ACell.Text = '') and (ACell.DateTime = 0);
end;
  
function ExposeBookSheet(ASpreadSheet: TcxSSBookSheet): TcxSSBookSheetAccess;
begin
  Result := TcxSSBookSheetAccess(ASpreadSheet);
end;

function ExposeDataStorage(ADataStorage: TcxSSDataStorage): TcxSSDataStorageAccess;
begin
  Result := TcxSSDataStorageAccess(ADataStorage);
end;

function ExposeSpreadSheetBook(ASpreadSheetBook: TcxCustomSpreadSheetBook): TcxCustomSpreadSheetBookAccess;
begin
  Result := TcxCustomSpreadSheetBookAccess(ASpreadSheetBook);
end;

function ExposeActiveSpreadSheet(ASpreadSheetBook: TcxCustomSpreadSheetBook): TcxSSBookSheetAccess;
begin
  Result := ExposeBookSheet(ExposeSpreadSheetBook(ASpreadSheetBook).ActiveSheet);
end;

function ExposeStyle(AStyle: TcxSSCellStyle): TcxSSCellStyleAccess;
begin
  Result := TcxSSCellStyleAccess(AStyle);
end;

function GetTextWidth(ACanvas: TdxPSReportRenderCustomCanvas;
  AFont: TFont; const S: string): Integer;
begin
  ACanvas.SaveState;
  try
    ACanvas.Font := AFont;
    Result := ACanvas.TextSize(S).cx;
  finally
    ACanvas.RestoreState;
  end;
end;
  
{ TdxReportCellSSString }  

constructor TdxReportCellSSString.Create(AParent: TdxReportCell);
begin
  inherited Create(AParent);
  PreventLeftTextExceed := False;
  TextAlignY := taBottom;
end;

procedure TdxReportCellSSString.Assign(Source: TPersistent);
begin
  inherited Assign(Source);
  if Source is TdxReportCellSSString then
  begin
    FBorders := TdxReportCellSSString(Source).FBorders;
    FBorderSlants  := TdxReportCellSSString(Source).FBorderSlants;
    FBorderSubs := TdxReportCellSSString(Source).FBorderSubs;
    FContentBkColor := TdxReportCellSSString(Source).FContentBkColor;
    FContentPattern := TdxReportCellSSString(Source).FContentPattern;
    FRealCol := TdxReportCellSSString(Source).FRealCol;
    FRealRow := TdxReportCellSSString(Source).FRealRow;
    FTextExtentLeft := TdxReportCellSSString(Source).FTextExtentLeft;
    FTextExtentRight := TdxReportCellSSString(Source).FTextExtentRight;
  end;
end;

procedure TdxReportCellSSString.SetBorders(AColor: TColor; APattern: TdxPSEdgePatternClass);
var 
  Side: TdxCellSide;
  ABorder: TdxPSCellBorder;
begin
  for Side := csLeft to csBottom do
  begin
    ABorder := Borders[Side];
    ABorder.Color := AColor;
    ABorder.Pattern := APattern;
    Borders[Side] := ABorder;
  end;
end;

procedure TdxReportCellSSString.DrawContent(
  ACanvas: TdxPSReportRenderCustomCanvas; AStage: TdxPSRenderStages);
begin
  if (rsFirstPass in AStage) and IsBackgroundDrawn then
    DrawBackground(ACanvas);
  
  if rsSecondPass in AStage then
  begin
    if IsTextDrawn then
      DrawText(ACanvas);
    if IsBordersDrawn then
      DrawBorders(ACanvas);
  end;  
end;

procedure TdxReportCellSSString.CalcBorderSubs(AnAdapter: TdxPSGridCellsAdapter);
    
  function GetNeighborCell(ASide: TdxCellSide): TdxReportCellSSString;
  begin
    Result := AnAdapter.GetNeighborCell(Self, ASide);
  end;

  function GetNeighborCellBorder(ASide, ABorderSide: TdxCellSide): TdxPSCellBorder;
  var
    Neighbor: TdxReportCellSSString;
  begin
    Neighbor := GetNeighborCell(ASide);
    if Neighbor <> nil then 
      Result := Neighbor.Borders[ABorderSide]
    else      
      Result := NullBorder;
  end;

  function GetNeighborCellBorderSalient(ASide, ABorderSide: TdxCellSide; 
    ASalient: TdxPSCellBorderSalientType): Integer;
  var
    Neighbor: TdxReportCellSSString;
  begin
    Neighbor := GetNeighborCell(ASide);
    if Neighbor <> nil then 
      Result := Neighbor.BorderEdgeSalients[ABorderSide, ASalient]
    else      
      Result := 0;
  end;
    
  procedure GetBordersAtCorner(ACorner: TdxPSCellCorner; out ABorders: TdxPSCellBorders);
  begin
    case ACorner of
      ccTopLeft:
        begin
          ABorders[csLeft] := GetNeighborCellBorder(csLeft, csTop);
          ABorders[csTop] := GetNeighborCellBorder(csTop, csLeft);
          ABorders[csRight] := Borders[csTop];
          ABorders[csBottom] := Borders[csLeft];
        end;
      ccTopRight:
        begin
          ABorders[csLeft] := Borders[csTop]; 
          ABorders[csTop] := GetNeighborCellBorder(csTop, csRight);
          ABorders[csRight] := GetNeighborCellBorder(csRight, csTop);
          ABorders[csBottom] := Borders[csRight];
        end;
      ccBottomRight:
        begin
          ABorders[csLeft] := Borders[csBottom]; 
          ABorders[csTop] := Borders[csRight];
          ABorders[csRight] := GetNeighborCellBorder(csRight, csBottom);
          ABorders[csBottom] :=  GetNeighborCellBorder(csBottom, csRight);
        end;
      ccBottomLeft:
        begin
          ABorders[csLeft] := GetNeighborCellBorder(csLeft, csBottom);
          ABorders[csTop] := Borders[csLeft];
          ABorders[csRight] := Borders[csBottom];
          ABorders[csBottom] := GetNeighborCellBorder(csBottom, csLeft);
        end;
    end;
  end;

  function DontNeedCornerSubsCalculation(ACorner: TdxPSCellCorner; ABorders: TdxPSCellBorders): Boolean;
  var
    BorderCount, DblCount: Integer;
    Color: TColor;
    Side: TdxCellSide;
    Border: TdxPSCellBorder;
  begin
    BorderCount := 0;
    DblCount := 0;
    Color := clBlack;
  
    Result := False;
    for Side := csLeft to csBottom do
    begin
      Border := ABorders[Side];
      if Border.Pattern = nil then Continue;
    
      Inc(BorderCount);
      if IsDoubleLineBorderPattern(Border) then 
        Inc(DblCount);

      if (DblCount <> 0) and (DblCount <> BorderCount) then 
        Exit;
      
      if BorderCount = 1 then 
        Color := Border.Color
      else
        if Color <> Border.Color then
          Exit;
    end;
    Result := True;
  end;
    
  function GetFavoriteSides(ACorner: TdxPSCellCorner; const ABorders: TdxPSCellBorders): TdxCellSides;
  const
    CornerSideMaps: array[TdxPSCellCorner, TdxCellSide] of TdxCellSides = 
      (([],         [],        [csTop],    [csLeft]), 
       ([csTop],    [],        [],         [csRight]), 
       ([csBottom], [csRight], [],         []), 
       ([],         [csLeft],  [csBottom], []));
  var
    StartSide, FavoriteSide, Side: TdxCellSide;
    DblCount: Integer;
    DblCandidates: TdxCellSides;
    Border, FavoriteBorder: TdxPSCellBorder;
  begin 
    StartSide := csLeft;
    while (StartSide < csBottom) and (ABorders[StartSide].Pattern = nil) do 
      Inc(StartSide);
    FavoriteSide := StartSide;
    FavoriteBorder := ABorders[FavoriteSide];
    
    DblCount := 0;
    DblCandidates := [];
    for Side := StartSide to csBottom do
    begin
       Border := ABorders[Side];
       if Border.Pattern = nil then Continue;

       if IsDoubleLineBorderPattern(Border) then
       begin
         Inc(DblCount);
         DblCandidates := DblCandidates + CornerSideMaps[ACorner, Side];
       end;  
       
       if Side > StartSide then
         if not IsDoubleLineBorderPattern(Border) and 
           (IsDoubleLineBorderPattern(FavoriteBorder) or 
           (Border.Pattern.Thickness > FavoriteBorder.Pattern.Thickness) or
           ((Border.Pattern.Thickness = FavoriteBorder.Pattern.Thickness) and 
           (Border.Color < FavoriteBorder.Color))) then
         begin 
           FavoriteBorder := Border;
           FavoriteSide := Side;
         end;  
    end;
    
    if DblCount > 1 then 
      Result := DblCandidates
    else
      Result := CornerSideMaps[ACorner, FavoriteSide];
  end;

  function CalcBorderSub(ASide: TdxCellSide; AEnd: TdxPSCellBorderEnd): Integer;
  const
    ConcurSides: array[TdxCellSide, TdxPSCellBorderEnd] of TdxCellSide = 
      ((csTop, csBottom), 
       (csLeft, csRight), 
       (csTop, csBottom), 
       (csLeft, csRight));
  var
    ConcurSide: TdxCellSide;
  begin
    ConcurSide := ConcurSides[ASide, AEnd];
    Result := BorderEdgeSalients[ConcurSide, bstOuter] + 
      Max(BorderEdgeSalients[ConcurSide, bstInner], GetNeighborCellBorderSalient(ASide, ConcurSide, bstInner));
  end;

const
  BorderEnds: array[TdxPSCellCorner, TdxCellSide] of TdxPSCellBorderEnd = 
   ((cbsTopLeft, cbsTopLeft, cbsTopLeft, cbsTopLeft), 
    (cbsTopLeft, cbsBottomRight, cbsTopLeft, cbsTopLeft), 
    (cbsBottomRight, cbsBottomRight, cbsBottomRight, cbsBottomRight), 
    (cbsBottomRight, cbsTopLeft, cbsTopLeft, cbsTopLeft));
  CornerSides: array[TdxPSCellCorner] of TdxCellSides = 
    ([csLeft, csTop], 
     [csTop, csRight], 
     [csRight, csBottom], 
     [csBottom, csLeft]);
var
  Corner: TdxPSCellCorner;
  Borders: TdxPSCellBorders;
  UnfavorableSides: TdxCellSides;
  Side: TdxCellSide;
  BorderEnd: TdxPSCellBorderEnd;
begin
  for Corner := ccTopLeft to ccBottomLeft do
  begin
    GetBordersAtCorner(Corner, Borders);
    if DontNeedCornerSubsCalculation(Corner, Borders) then
      Continue;
      
    UnfavorableSides := CornerSides[Corner] - GetFavoriteSides(Corner, Borders);
    for Side := csLeft to csBottom do 
      if Side in UnfavorableSides then
      begin
        BorderEnd := BorderEnds[Corner, Side];
        BorderSubs[Side, BorderEnd] := CalcBorderSub(Side, BorderEnd);
      end;  
  end;  
end;

procedure TdxReportCellSSString.CalcDoubleBorderSlants(AnAdapter: TdxPSGridCellsAdapter);
var
  Neighbor: TdxReportCellSSString;
begin
  if IsDoubleLineBorderPattern(csLeft) then
  begin
    Neighbor := AnAdapter.GetNeighborCell(Self, csLeft);
    BorderSlants[csLeft, cbcTopLeft] := Ord((Neighbor <> nil) and Neighbor.IsDoubleLineBorderPattern(csTop));
    BorderSlants[csLeft, cbcTopRight] := Ord(IsDoubleLineBorderPattern(csTop));    
    BorderSlants[csLeft, cbcBottomRight] := Ord(IsDoubleLineBorderPattern(csBottom));    
    BorderSlants[csLeft, cbcBottomLeft] := Ord((Neighbor <> nil) and Neighbor.IsDoubleLineBorderPattern(csBottom));
  end;

  if IsDoubleLineBorderPattern(csTop) then
  begin
    Neighbor := AnAdapter.GetNeighborCell(Self, csTop);    
    BorderSlants[csTop, cbcTopLeft] := Ord((Neighbor <> nil) and Neighbor.IsDoubleLineBorderPattern(csLeft));
    BorderSlants[csTop, cbcTopRight] := Ord((Neighbor <> nil) and Neighbor.IsDoubleLineBorderPattern(csRight));    
    BorderSlants[csTop, cbcBottomRight] := Ord(IsDoubleLineBorderPattern(csRight));    
    BorderSlants[csTop, cbcBottomLeft] := Ord(IsDoubleLineBorderPattern(csLeft));
  end;

  if IsDoubleLineBorderPattern(csRight) then
  begin
    Neighbor := AnAdapter.GetNeighborCell(Self, csRight);
    BorderSlants[csRight, cbcTopLeft] := Ord(IsDoubleLineBorderPattern(csTop));
    BorderSlants[csRight, cbcTopRight] := Ord((Neighbor <> nil) and Neighbor.IsDoubleLineBorderPattern(csTop));
    BorderSlants[csRight, cbcBottomRight] := Ord((Neighbor <> nil) and Neighbor.IsDoubleLineBorderPattern(csBottom));
    BorderSlants[csRight, cbcBottomLeft] := Ord(IsDoubleLineBorderPattern(csBottom));
  end;

  if IsDoubleLineBorderPattern(csBottom) then
  begin
    Neighbor := AnAdapter.GetNeighborCell(Self, csBottom);
    BorderSlants[csBottom, cbcTopLeft] := Ord(IsDoubleLineBorderPattern(csLeft));
    BorderSlants[csBottom, cbcTopRight] := Ord(IsDoubleLineBorderPattern(csRight));
    BorderSlants[csBottom, cbcBottomRight] := Ord((Neighbor <> nil) and Neighbor.IsDoubleLineBorderPattern(csRight));
    BorderSlants[csBottom, cbcBottomLeft] := Ord((Neighbor <> nil) and Neighbor.IsDoubleLineBorderPattern(csLeft));
  end;  
end;

procedure TdxReportCellSSString.ConvertCoords(APixelsNumerator, APixelsDenominator: Integer);
begin
  inherited ConvertCoords(APixelsNumerator, APixelsDenominator);
  TextExtentLeft := MulDiv(TextExtentLeft, APixelsNumerator, APixelsDenominator);
  TextExtentRight := MulDiv(TextExtentRight, APixelsNumerator, APixelsDenominator);
end;

function TdxReportCellSSString.GetBackgroundBounds(ACanvas: TdxPSReportRenderCustomCanvas): TRect;
var
  LineThickness: Integer;
begin
  Result := inherited GetBackgroundBounds(ACanvas);
  if not IsFixed then 
  begin
    LineThickness := Self.LineThickness;
    if BorderEdgeSalients[csLeft, bstOuter] > 0 then
      Dec(Result.Left, LineThickness);
    if BorderEdgeSalients[csTop, bstOuter] > 0 then
      Dec(Result.Top, LineThickness);
    if BorderEdgeSalients[csRight, bstOuter] > 0 then
      Inc(Result.Right, LineThickness);
    if BorderEdgeSalients[csBottom, bstOuter] > 0 then
      Inc(Result.Bottom, LineThickness);
    ACanvas.FixupRect(Result);
  end;  
end;

function TdxReportCellSSString.GetBorderEdgeBounds(ASide: TdxCellSide; const AOuterRect: TRect): TRect;
begin  
  Result := inherited GetBorderEdgeBounds(ASide, AOuterRect);
  with Result do 
    if ASide in csLeftRight then
    begin
      Inc(Top, LineThickness * BorderSubs[ASide, cbsTopLeft]);
      Dec(Bottom, LineThickness * BorderSubs[ASide, cbsBottomRight]);
    end
    else
    begin
      Inc(Left, LineThickness * BorderSubs[ASide, cbsTopLeft]);
      Dec(Right, LineThickness * BorderSubs[ASide, cbsBottomRight]);
    end;
end;

function TdxReportCellSSString.GetBorderBrush(ASide: TdxCellSide): TBrush;
var
  AItem: TdxPSEdgePatternItem;
begin
  AItem := dxPSEdgePatternFactory.Items[TdxPSEdgePatternClass(BorderEdgeClasses[ASide]), IsPrinting];
  Result := AItem.Brushes[dxCellEdgeSideOrientation[ASide]];
end;

function TdxReportCellSSString.GetBorderEdgeClass(ASide: TdxCellSide): TdxPSCellBorderClass;
begin
  Result := Borders[ASide].Pattern;
  if Result = nil then 
    Result := TdxPSSolidEdgePattern;
end;

function TdxReportCellSSString.GetEffectiveBounds(
  ACanvas: TdxPSReportRenderCustomCanvas; AStage: TdxPSRenderStages): TRect;
begin
  if rsFirstPass in AStage then 
    Result := inherited GetEffectiveBounds(ACanvas, AStage)
  else
    UnionRect(Result, GetOuterBounds(ACanvas), GetTextBounds(ACanvas));
end;

function TdxReportCellSSString.GetTextBounds(ACanvas: TdxPSReportRenderCustomCanvas): TRect;
begin
  Result := inherited GetTextBounds(ACanvas);
  if TextExtentLeft <> 0 then 
    Result.Left := TextExtentLeft;
  if TextExtentRight <> 0 then 
    Result.Right := TextExtentRight;
end;

function TdxReportCellSSString.IsDrawn(ACanvas: TdxPSReportRenderCustomCanvas;
  AStage: TdxPSRenderStages; const ARect: TRect): Boolean;
var
  R: TRect;
begin
  Result := IntersectRect(R, GetAbsoluteInnerBounds(ACanvas), ARect);
end;

function TdxReportCellSSString.GetBorderPainterClass: TdxPSCellBorderPainterClass;
begin
  if IsFixed then 
    Result := inherited GetBorderPainterClass
  else
    Result := TdxPSCellPatternsBorderPainter;
end;

procedure TdxReportCellSSString.InitBorderPainter(ABordersPainter: TdxPSCellBorderPainter);
begin
  inherited InitBorderPainter(ABordersPainter);
  if not IsFixed then 
    TdxPSCellPatternsBorderPainter(ABordersPainter).FGridAdapter := 
      TCustomdxSpreadSheetReportLink(ReportCells.ReportLink).FGridAdapter;
end;

function TdxReportCellSSString.GetContentBkColor: TColor;
begin
  Result := FContentBkColor;
end;

function TdxReportCellSSString.GetContentPattern: TdxPSFillPatternClass;
begin
  Result := FContentPattern;
end;

procedure TdxReportCellSSString.SetContentBkColor(Value: TColor);
begin
  FContentBkColor := Value;
end;

procedure TdxReportCellSSString.SetContentPattern(Value: TdxPSFillPatternClass);
begin
  FContentPattern := Value;
end;

procedure TdxReportCellSSString.ReadBorders(AReader: TdxPSDataReader);
var
  Side: TdxCellSide;
  Border: TdxPSCellBorder;
begin
  for Side := csLeft to csBottom do 
  begin
    Border.Color := AReader.ReadInteger;
    Border.Pattern := TdxPSEdgePatternClass(AReader.ReadCellBorderClass);

    Borders[Side] := Border;
  end;  
end;

procedure TdxReportCellSSString.ReadData(AReader: TdxPSDataReader);
begin
  inherited ReadData(AReader);
  with AReader do
  begin
    ReadBorders(AReader);
    FBorderSlants := ReadInteger;
    FBorderSubs := ReadInteger;
    RealCol := ReadInteger;
    RealRow := ReadInteger;
    TextExtentLeft := ReadInteger;
    TextExtentRight := ReadInteger;    
  end;
end;

procedure TdxReportCellSSString.WriteBorders(AWriter: TdxPSDataWriter);
var
  Side: TdxCellSide;
begin
  for Side := csLeft to csBottom do 
  begin
    AWriter.WriteInteger(Borders[Side].Color);
    AWriter.WriteClassName(Borders[Side].Pattern);
  end;
end;

procedure TdxReportCellSSString.WriteData(AWriter: TdxPSDataWriter);
begin
  inherited WriteData(AWriter);
  with AWriter do
  begin
    WriteBorders(AWriter);
    WriteInteger(FBorderSlants);
    WriteInteger(FBorderSubs);
    WriteInteger(RealCol);
    WriteInteger(RealRow);
    WriteInteger(TextExtentLeft);
    WriteInteger(TextExtentRight);
  end;
end;

function TdxReportCellSSString.GetAbsoluteInnerBounds(ACanvas: TdxPSReportRenderCustomCanvas): TRect;
begin
  Result := GetInnerBounds(ACanvas);
  if Parent <> nil then
    with Parent.AbsoluteOrigin do
      OffsetRect(Result, X, Y);
end;

function TdxReportCellSSString.GetDoubleBorderRgn(ASide: TdxCellSide; const R: TRect): HRGN;
const
  VertexCount = 4;
type
  PdxPSCellEdgeCoords = ^TdxPSCellEdgeCoords;
  TdxPSCellEdgeCoords = record
    case Byte of 
      0: (TopLeft, TopRight, BottomRight, BottomLeft: TPoint);            
      1: (Points: array[0..VertexCount - 1] of TPoint);
  end;
var
  EdgeCoords: TdxPSCellEdgeCoords;
  Thickness: Integer;
begin
  with EdgeCoords do
  begin
    TopLeft := R.TopLeft;
    TopRight := Point(R.Right, R.Top);
    BottomRight := R.BottomRight;
    BottomLeft := Point(R.Left, R.Bottom);
  end;  

  Thickness := LineThickness * (TdxPSDoubleLineSolidEdgePattern.Thickness - 1);
    
  with EdgeCoords do 
    case ASide of
      csLeft:
        begin
          Inc(TopLeft.Y, Thickness * BorderSlants[ASide, cbcTopLeft]);
          Inc(TopRight.Y, Thickness * BorderSlants[ASide, cbcTopRight]);
          Dec(BottomRight.Y, Thickness * BorderSlants[ASide, cbcBottomRight]);
          Dec(BottomLeft.Y, Thickness * BorderSlants[ASide, cbcBottomLeft]);
        end;
      csTop:
        begin
          Inc(TopLeft.X, Thickness * BorderSlants[ASide, cbcTopLeft]);
          Dec(TopRight.X, Thickness * BorderSlants[ASide, cbcTopRight]);
          Dec(BottomRight.X, Thickness * BorderSlants[ASide, cbcBottomRight]);
          Inc(BottomLeft.X, Thickness * BorderSlants[ASide, cbcBottomLeft]);
        end;
      csRight:
        begin
          Inc(TopLeft.Y, Thickness * BorderSlants[ASide, cbcTopLeft]);
          Inc(TopRight.Y, Thickness * BorderSlants[ASide, cbcTopRight]);
          Dec(BottomRight.Y, Thickness * BorderSlants[ASide, cbcBottomRight]);
          Dec(BottomLeft.Y, Thickness * BorderSlants[ASide, cbcBottomLeft]);
        end;
      csBottom:
        begin
          Inc(TopLeft.X, Thickness * BorderSlants[ASide, cbcTopLeft]);
          Dec(TopRight.X, Thickness * BorderSlants[ASide, cbcTopRight]);
          Dec(BottomRight.X, Thickness * BorderSlants[ASide, cbcBottomRight]);
          Inc(BottomLeft.X, Thickness * BorderSlants[ASide, cbcBottomLeft]);
        end;
    end;
   
  Result := CreatePolygonRgn(EdgeCoords.Points, SizeOf(TdxPSCellEdgeCoords) div SizeOf(TPoint), Windows.WINDING);
end;

function TdxReportCellSSString.IsDoubleLineBorderPattern(ABorder: TdxPSCellBorder): Boolean;
begin
  with ABorder do
    Result := (Pattern <> nil) and Pattern.InheritsFrom(TdxPSDoubleLineSolidEdgePattern);
end;

function TdxReportCellSSString.IsDoubleLineBorderPattern(ASide: TdxCellSide): Boolean;
begin
  Result := (ASide in CellSides) and IsDoubleLineBorderPattern(Self.Borders[ASide]);
end;
  
function TdxReportCellSSString.NullBorder: TdxPSCellBorder;
begin
  FillChar(Result, SizeOf(TdxPSCellBorder), 0);
end;

function TdxReportCellSSString.GetBorder(ASide: TdxCellSide): TdxPSCellBorder;
begin
  Result := FBorders[ASide];
end;

function TdxReportCellSSString.GetBordersBkColor: TColor;
begin
  if Transparent then
    Result := ColorToRGB(clWindow)
  else
    Result := Color;
end;
  
function TdxReportCellSSString.GetBorderSlant(ASide: TdxCellSide; ACorner: TdxPSCellBorderCorner): Integer;
begin
  Result := (FBorderSlants shr GetBorderSlantOffset(ASide, ACorner)) and SlantMask;
end;

function TdxReportCellSSString.GetBorderSlantOffset(ASide: TdxCellSide; ACorner: TdxPSCellBorderCorner): TDWORDBits;
begin
  Result := Integer(ASide) * SlantBitsPerSide + Integer(ACorner);
end;

function TdxReportCellSSString.GetBorderSub(ASide: TdxCellSide; AEnd: TdxPSCellBorderEnd): TdxPSCellBorderSub;
begin
  Result := (FBorderSubs shr GetBorderSubOffset(ASide, AEnd)) and SubEndMask;
end;

function TdxReportCellSSString.GetBorderSubMask(ASide: TdxCellSide; AEnd: TdxPSCellBorderEnd): DWORD;
begin
  Result := 0 or (SubEndMask shl GetBorderSubOffset(ASide, AEnd));
end;

function TdxReportCellSSString.GetBorderSubOffset(ASide: TdxCellSide; AEnd: TdxPSCellBorderEnd): TDWORDBits;
begin
  Result := Integer(ASide) * SubBitsPerSide + SubBitsPerEnd * Integer(AEnd);
end;

function TdxReportCellSSString.GetClipContent: Boolean;
begin
  Result := (Format and dxFormatClipContent) = dxFormatClipContent;
end;                    

function TdxReportCellSSString.GetCol: Integer;
begin
  Result := Index;
end;

function TdxReportCellSSString.GetFill: Boolean;
begin
  Result := (Format and dxFormatFill) = dxFormatFill;
end;                    

function TdxReportCellSSString.GetIsFixed: Boolean;  
begin
  Result := (Format and dxFormatFixed) = dxFormatFixed;
end;                    

function TdxReportCellSSString.GetIsMerged: Boolean;
begin
  Result := (Format and dxFormatMerged) = dxFormatMerged;
end;

function TdxReportCellSSString.GetIsNearMostLeft: Boolean;
begin
  Result := (Format and dxFormatNearMostLeft) = dxFormatNearMostLeft;
end;

function TdxReportCellSSString.GetIsNearMostTop: Boolean;
begin
  Result := (Format and dxFormatNearMostTop) = dxFormatNearMostTop;
end;

function TdxReportCellSSString.GetIsNearMostTopOrLeft: Boolean;
begin
  Result := GetIsNearMostLeft or GetIsNearMostTop;
end;

function TdxReportCellSSString.GetIsVirtual: Boolean;
begin
  Result := GetFormatBit(dxFormatVirtual);
end;

function TdxReportCellSSString.GetRow: Integer;
begin
  Result := Parent.Index;
end;

procedure TdxReportCellSSString.SetBorder(ASide: TdxCellSide; Value: TdxPSCellBorder);
begin
  FBorders[ASide] := Value;
end;

procedure TdxReportCellSSString.SetBorderSlant(ASide: TdxCellSide;
  ACorner: TdxPSCellBorderCorner; Value: Integer);
var
  Mask: DWORD;
begin
  Mask := 1 shl GetBorderSlantOffset(ASide, ACorner);
  FBorderSlants := FBorderSlants and not Mask;
  if Value <> 0 then 
    FBorderSlants := FBorderSlants or Mask;
end;

procedure TdxReportCellSSString.SetBorderSub(ASide: TdxCellSide; AEnd: TdxPSCellBorderEnd; 
  Value: TdxPSCellBorderSub);
begin
  FBorderSubs := FBorderSubs and not GetBorderSubMask(ASide, AEnd) or 
    (Value shl GetBorderSubOffset(ASide, AEnd));
end;

procedure TdxReportCellSSString.SetClipConent(Value: Boolean);
const
  dxClipContent: array[Boolean] of DWORD = (0, dxFormatClipContent);
begin
  Format := Format and not dxFormatClipContent or dxClipContent[Value];
end;

procedure TdxReportCellSSString.SetFill(Value: Boolean);
const
  dxFill: array[Boolean] of DWORD = (0, dxFormatFill);
begin
  Format := Format and not dxFormatFill or dxFill[Value];
end;

procedure TdxReportCellSSString.SetIsFixed(Value: Boolean);
const
  dxFixed: array[Boolean] of DWORD = (0, dxFormatFixed);
begin
  Format := Format and not dxFormatFixed or dxFixed[Value];
end;

procedure TdxReportCellSSString.SetIsMerged(Value: Boolean);
const
  dxMerged: array[Boolean] of DWORD = (0, dxFormatMerged);
begin
  Format := Format and not dxFormatMerged or dxMerged[Value];
end;

procedure TdxReportCellSSString.SetIsNearMostLeft(Value: Boolean);
const
  dxIsNearMostLeft: array[Boolean] of DWORD = (0, dxFormatNearMostLeft);
begin
  Format := Format and not dxFormatNearMostLeft or dxIsNearMostLeft[Value];
end;

procedure TdxReportCellSSString.SetIsNearMostTop(Value: Boolean);
const
  dxIsNearMostTop: array[Boolean] of DWORD = (0, dxFormatNearMostTop);
begin
  Format := Format and not dxFormatNearMostTop or dxIsNearMostTop[Value];
end;

procedure TdxReportCellSSString.SetIsVirtual(Value: Boolean);
begin
  SetFormatBit(dxFormatVirtual, Value);
end;

{ TdxPSCellPatternsBorderPainter }

function TdxPSCellPatternsBorderPainter.Item: TdxReportCellSSString;
begin
  Result := inherited Item as TdxReportCellSSString;
end;

procedure TdxPSCellPatternsBorderPainter.ClipItemBounds(
  ACanvas: TdxPSReportRenderCustomCanvas; AOuterRect: TRect);
begin
  with Item do
  begin
    if IsNearMostTop then
      Inc(AOuterRect.Top, LineThickness * BorderEdgeSalients[csTop, bstOuter]);
    if IsNearMostLeft then
      Inc(AOuterRect.Left, LineThickness * BorderEdgeSalients[csLeft, bstOuter]);
  end;
  ACanvas.IntersectClipRgn(AOuterRect)
end;

procedure TdxPSCellPatternsBorderPainter.DrawBorder(
  ACanvas: TdxPSReportRenderCustomCanvas; const R: TRect; ASide: TdxCellSide);
var
  ABorder: TdxPSCellBorder;
  ARegion: TcxRegionHandle;
begin
  if Item.IsDoubleLineBorderPattern(ASide) then
    ARegion := Item.GetDoubleBorderRgn(ASide, R)
  else
    ARegion := CreateRectRgnIndirect(R);

  ABorder := Item.Borders[ASide];
  ACanvas.FillEdge(ARegion, Item.BordersBkColor,
    ABorder.Color, ASide in [csLeft, csRight], ABorder.Pattern);
  DeleteObject(ARegion);
end;

procedure TdxPSCellPatternsBorderPainter.DrawBorders(
  ACanvas: TdxPSReportRenderCustomCanvas; const R: TRect);
var
  ABorder: TdxPSCellBorder;
  ABorderRect: TRect;
  ABrushOrg: TPoint;
  ASide: TdxCellSide;
  ATempRect: TRect;
begin
  ACanvas.SaveClipRgn;
  try
    if Item.IsNearMostTopOrLeft then
      ClipItemBounds(ACanvas, R);

    for ASide := Low(TdxCellSide) to High(TdxCellSide) do
      if ASide in Item.CellSides then
      begin
        ABorderRect := Item.GetBorderEdgeBounds(ASide, R);
        if ACanvas.IsRectVisible(ABorderRect) then
        begin
          ABrushOrg := ACanvas.BrushOrg;
          try
            ABorder := Item.Borders[ASide];
            if (ABorder.Pattern <> nil) and ABorder.Pattern.RequiredBrushOrigin then
            begin
              ATempRect := ABorderRect;
              ACanvas.LogicalToDeviceCoordinates(ATempRect);
              ACanvas.BrushOrg := Point(ATempRect.Left mod 2, ATempRect.Top mod 2);
            end;
            ACanvas.FixupRect(ABorderRect);
            DrawBorder(ACanvas, ABorderRect, ASide);
          finally
            ACanvas.BrushOrg := ABrushOrg;
          end;
        end;
      end;
  finally
    ACanvas.RestoreClipRgn;
  end;
end;

{ TdxPSGridCellsAdapter }

constructor TdxPSGridCellsAdapter.Create(AReportCells: TdxReportCells);
begin
  inherited Create;
  FReportCells := AReportCells;
end;

function TdxPSGridCellsAdapter.GetNeighborCell(AItem: TdxReportCellSSString; 
  ASide: TdxCellSide): TdxReportCellSSString;
begin
  Result := nil;
  case ASide of 
    csLeft:
      if AItem.Col > 0 then
        Result := Cells[AItem.Col - 1, AItem.Row];
    csTop:
      if AItem.Row > 0 then
        Result := Cells[AItem.Col, AItem.Row - 1];
    csRight:
      if AItem.Col + 1 < ColCount then
        Result := Cells[AItem.Col + 1, AItem.Row];
    csBottom:
      if AItem.Row + 1 < RowCount then
        Result := Cells[AItem.Col, AItem.Row + 1];
  end;
end;

function TdxPSGridCellsAdapter.GetCell(Col, Row: Integer): TdxReportCellSSString;
begin                                                 
  Result := FReportCells.Cells[Row].DataItems[Col] as TdxReportCellSSString;
end;

function TdxPSGridCellsAdapter.GetColCount: Integer;
begin
  with FReportCells do 
    if Cells.CellCount > 0 then
      Result := Cells[0].DataItemCount
    else
      Result := 0;  
end;

function TdxPSGridCellsAdapter.GetColOffset(Index: Integer): Integer;
begin
  if Index < ColCount then 
    Result := Cells[Index, 0].Left
  else
    Result := Cells[ColCount - 1, 0].Left + Cells[ColCount - 1, 0].Width;
end;

function TdxPSGridCellsAdapter.GetColWidth(Index: Integer): Integer;
begin
  Result := Cells[Index, 0].Width;
end;

function TdxPSGridCellsAdapter.GetRow(Index: Integer): TdxReportCell;
begin
  Result := FReportCells.Cells[Index];
end;

function TdxPSGridCellsAdapter.GetRowCount: Integer;
begin
  Result := FReportCells.Cells.CellCount;
end;

function TdxPSGridCellsAdapter.GetRowHeight(Index: Integer): Integer;    
begin
  Result := Cells[0, Index].Height;
end;

function TdxPSGridCellsAdapter.GetRowIndex(Index: Integer): Integer;
begin
  Result := Rows[Index].Data;
end;

function TdxPSGridCellsAdapter.GetRowOffset(Index: Integer): Integer;
begin
  if Index < RowCount then
    Result := Cells[0, Index].Top
  else
    Result := Cells[0, RowCount - 1].Top + Cells[0, RowCount - 1].Height;
end;

{ TdxPSSSStringGridCellDataMap }

class procedure TdxPSSSStringGridCellDataMap.InitializeCellData(ACol, ARow: Integer; 
  ADataItem: TAbstractdxReportCellData; AReportLink: TAbstractdxGridReportLink);
begin
  inherited;
  with TCustomdxSpreadSheetReportLink(AReportLink) do 
  begin
    TdxReportCellSSString(ADataItem).Fill := not IsFixedCell(ACol, ARow) and 
      (GetSSCellObject(ACol, ARow).DisplayTextAlignment = dtaFILL);
    TdxReportCellSSString(ADataItem).IsFixed := IsFixedCell(ACol, ARow);
    TdxReportCellSSString(ADataItem).IsMerged := IsMergedCell(ACol, ARow);
    TdxReportCellSSString(ADataItem).IsNearMostLeft := IsNearMostLeftCell(ACol, ARow);
    TdxReportCellSSString(ADataItem).IsNearMostTop := IsNearMostTopCell(ACol, ARow);
    TdxReportCellSSString(ADataItem).RealCol := ACol;
    TdxReportCellSSString(ADataItem).RealRow := ARow;

    SetupCellBorders(ACol, ARow, ADataItem);
  end;  
end;

class function TdxPSSSStringGridCellDataMap.DataClass: TdxReportCellDataClass;
begin
  Result := TdxReportCellSSString;
end;

{ TCustomdxSpreadSheetReportLink }

constructor TCustomdxSpreadSheetReportLink.Create(AOwner: TComponent);
begin
  inherited;
  FColCount := -1;
  FRowCount := -1;  
  FCellObjects := TList.Create;
  FGridAdapter := TdxPSGridCellsAdapter.Create(nil);
  FTempFont := TFont.Create;
end;

destructor TCustomdxSpreadSheetReportLink.Destroy;
begin
  FTempFont.Free;
  FGridAdapter.Free;
  DeleteCellObjects;
  FCellObjects.Free;
  inherited;
end;  

procedure TCustomdxSpreadSheetReportLink.Assign(Source: TPersistent);
begin
  inherited;
  if Source is TCustomdxSpreadSheetReportLink then
    with TCustomdxSpreadSheetReportLink(Source) do 
    begin
      Self.OptionsView := OptionsView;
      Self.PrintArea := PrintArea;
    end;  
end;

procedure TCustomdxSpreadSheetReportLink.ClearPrintArea;
begin
  PrintArea := dxPSEmptySSPrintArea;
end;

function TCustomdxSpreadSheetReportLink.DataProviderPresent: Boolean;
begin
  Result := inherited DataProviderPresent and DataToPrintExist;
end;

function TCustomdxSpreadSheetReportLink.DataToPrintExist: Boolean;
begin
  Result := (MeaningColCount > 0) and (MeaningRowCount > 0);
end;

function TCustomdxSpreadSheetReportLink.PrintAreaExists: Boolean; 
begin
  Result := not EqualRect(PrintArea, dxPSEmptySSPrintArea);
end;

function TCustomdxSpreadSheetReportLink.CannotActivateReportErrorString: string;
begin
  if not inherited DataProviderPresent then
    Result := inherited CannotActivateReportErrorString
  else
    Result := cxGetResourceString(@sdxDataToPrintDoesNotExist);
end;

procedure TCustomdxSpreadSheetReportLink.DoChangeComponent;
begin
  ClearPrintArea;
  inherited;
end;

procedure TCustomdxSpreadSheetReportLink.InternalRestoreDefaults;
begin
  inherited;
  EndEllipsis := False;
  Soft3D := True;
  FPrintArea := dxPSEmptySSPrintArea;
  OptionsView := [ssovRowAndColumnHeadings];
end;

procedure TCustomdxSpreadSheetReportLink.InternalRestoreFromOriginal;
begin
  inherited;
  FixedFont := ExposeSpreadSheetBook(CustomSpreadSheetBook).HeaderFont;
end;

function TCustomdxSpreadSheetReportLink.IsScaleGridLines: Boolean;
begin
  Result := False;
end;

function TCustomdxSpreadSheetReportLink.NeedTwoPassRendering: Boolean;
begin
  Result := True;
end;

function TCustomdxSpreadSheetReportLink.GetDataItemClass(ACol: Integer; ARow: Integer = 0): TdxReportCellDataClass;
begin
  Result := TdxReportCellSSString;
end;

function TCustomdxSpreadSheetReportLink.GetColCount: Integer;
var
  BeginCol, BeginRow, EndRow: Integer;
begin
  if FColCount <= 0 then
    if HasSelection then 
    begin
      GetSelectedRange(BeginCol, FColCount, BeginRow, EndRow);
      Inc(FColCount);
    end
    else
      FColCount := MeaningColCount + 1;
  Result := FColCount;
end;

function TCustomdxSpreadSheetReportLink.GetFixedColCount: Integer;
begin
  Result := 1;
end;

function TCustomdxSpreadSheetReportLink.GetFixedRowCount: Integer;
begin
  Result := 1;
end;

function TCustomdxSpreadSheetReportLink.GetRowCount: Integer;
var
  BeginCol, EndCol, BeginRow: Integer;
begin
  if FRowCount <= 0 then
    if HasSelection then 
    begin
      GetSelectedRange(BeginCol, EndCol, BeginRow, FRowCount);
      Inc(FRowCount);
    end  
    else
      FRowCount := MeaningRowCount + 1;
  Result := FRowCount;
end;

function TCustomdxSpreadSheetReportLink.GetCellSides(ACol, ARow: Integer): TdxCellSides;

  function AreNeigborhoodsTransparent(ASide: TdxCellSide): Boolean;
  begin
    case ASide of
      csLeft: 
        Result := IsNearMostLeftCell(ACol, ARow) or 
          (GetCellTransparent(ACol - 1, ARow) and GetCellTransparent(ACol, ARow));
      csTop:  
        Result := IsNearMostTopCell(ACol, ARow) or 
          (GetCellTransparent(ACol, ARow - 1) and GetCellTransparent(ACol, ARow));    
      csRight:
        Result := ((ACol = GetColCount - 1) {and not FAppendingExtraColumns}) or
          (GetCellTransparent(ACol + 1, ARow) and GetCellTransparent(ACol, ARow));          
      else // csBottom    
        Result := ((ARow = GetRowCount - 1) {and not FAppendingExtraColumns}) or
          (GetCellTransparent(ACol, ARow + 1) and GetCellTransparent(ACol, ARow)); 
    end;    
  end;
  
var
  Style: TcxSSCellStyle;
  Side: TdxCellSide;
  BorderStyle: TcxSSEdgeLineStyle;
begin
  if IsFixedCell(ACol, ARow) or IsSuppressSourceFormats then
    Result := inherited GetCellSides(ACol, ARow)
  else
  begin
    Result := csAll;
    Style := GetSSCellStyle(ACol, ARow);

    for Side := Low(TdxCellSide) to High(TdxCellSide) do
    begin
      if Style <> nil then 
        BorderStyle := Style.Borders.Edges[cxSSEdgeBorderMap[Side]].Style
      else
        BorderStyle := lsDefault;

      if (BorderStyle in [lsNone, lsDefault]) and 
        (not IsShowGridLines or not AreNeigborhoodsTransparent(Side) or
        (IsMergedCell(ACol, ARow) and not IsMergedBorder(ACol, ARow, Side))) then 
        Exclude(Result, Side);
    end;    
  end;
end;

function TCustomdxSpreadSheetReportLink.GetCellText(ACol, ARow: Integer): string;
begin
  if IsFixedCell(ACol, ARow) then
  begin
    if (ACol = 0) and (ARow <> 0) then 
      Result := TcxSSUtils.RowNameByIndex(ARow - 1, CustomSpreadSheetBook.R1C1ReferenceStyle);
    if (ARow = 0) and (ACol <> 0) then 
      Result := TcxSSUtils.ColumnNameByIndex(ACol - 1, CustomSpreadSheetBook.R1C1ReferenceStyle);
  end    
  else  
    if (FProcessingMerges or not IsMergedCell(ACol, ARow)) and not FAppendingExtraColumns then 
      Result := GetSSCellObject(ACol, ARow).DisplayText
    else
      Result := '';
end;

function TCustomdxSpreadSheetReportLink.GetCellTextAlignY(ACol, ARow: Integer): TcxTextAlignY;
begin
  if (DrawMode <> gdmBorrowSource) and not IsFixedCell(ACol, ARow) then 
    Result := taBottom
  else
    Result := inherited GetCellTextAlignY(ACol, ARow);
end;

function TCustomdxSpreadSheetReportLink.GetMinRowHeight(
  ACanvas: TdxPSReportRenderCustomCanvas; AFont: TFont): Integer;
begin
  Result := 1;
end;

function TCustomdxSpreadSheetReportLink.GetSelectionRect: TRect;
begin
  if OnlySelected then 
    Result := ExposeBookSheet(BookSheet).SelectionRect
  else
    Result := PrintArea;
  OffsetRect(Result, 1, 1);
end;

function TCustomdxSpreadSheetReportLink.GetSourceCellColor(ACol, ARow: Integer): TColor;
begin
  if IsFixedCell(ACol, ARow) then 
    Result := ColorToRGB(ExposeSpreadSheetBook(CustomSpreadSheetBook).HeaderColor)
  else  
    if GetSourceCellContentPattern(ACol, ARow).InheritsFrom(TdxPSSolidFillPattern) then 
      Result := GetRealColor(GetSSCellStyle(ACol, ARow).Brush.BackgroundColor, clWindow)
    else  
      Result := GetRealColor(GetSSCellStyle(ACol, ARow).Brush.ForegroundColor, clWindow);
end;

function TCustomdxSpreadSheetReportLink.GetSourceCellContentBkColor(ACol, ARow: Integer): TColor;
begin
  if IsFixedCell(ACol, ARow) then 
    Result := ColorToRGB(ExposeSpreadSheetBook(CustomSpreadSheetBook).HeaderColor)
  else  
    Result := GetRealColor(GetSSCellStyle(ACol, ARow).Brush.BackgroundColor, clWindow);
end;

function TCustomdxSpreadSheetReportLink.GetSourceCellContentPattern(ACol, ARow: Integer): TdxPSFillPatternClass;
begin
  if IsFixedCell(ACol, ARow) then
    Result := TdxPSSolidFillPattern
  else
    Result := dxPSFillPatternClassMap[GetSSCellStyle(ACol, ARow).Brush.Style];
end;

function TCustomdxSpreadSheetReportLink.GetSourceCellEdge3DSoft(ACol, ARow: Integer): Boolean;
begin
  Result := True;
end;
 
function TCustomdxSpreadSheetReportLink.GetSourceCellEdgeMode(ACol, ARow: Integer): TdxCellEdgeMode;
const
  FixedEdgeModes: array[Boolean] of TdxCellEdgeMode = (cem3DEffects, cemPattern); 
begin
  if IsFixedCell(ACol, ARow) then
    Result := FixedEdgeModes[ExposeSpreadSheetBook(CustomSpreadSheetBook).PainterType = ptOfficeXPStyle]
  else
    Result := cemPattern;
end;

function TCustomdxSpreadSheetReportLink.GetSourceCellFontIndex(ACol, ARow: Integer): Integer;

  procedure SetupFont(AFontRec: PcxSSFontRec; AFont: TFont);
  begin
    with AFont do
    begin
      Name := AFontRec.Name;
      Color := GetRealColor(AFontRec.FontColor, Self.Font.Color);
      Style := AFontRec.Style;
      Charset := AFontRec.Charset;
      Size := AFontRec.Size;
    end;  
    
    if not dxIsTrueTypeFont(AFont) then AFont.Name := Self.Font.Name;      
  end;

begin
  if not IsFixedCell(ACol, ARow) then 
  begin
    SetupFont(ExposeStyle(GetSSCellStyle(ACol, ARow)).StylePtr^.FontPtr, FTempFont);
    Result := AddFontToPool(FTempFont);
  end
  else
    Result := FSourceHeaderFontIndex;
end;

function TCustomdxSpreadSheetReportLink.GetSourceCellMultiline(ACol, ARow: Integer): Boolean;
begin
  Result := not IsFixedCell(ACol, ARow) and GetSSCellStyle(ACol, ARow).WordBreak;
end;

function TCustomdxSpreadSheetReportLink.GetSourceCellTextAlignX(ACol, ARow: Integer): TcxTextAlignX;
begin
  if IsFixedCell(ACol, ARow) then
    Result := taCenterX
  else
    Result := dxPSTextAlignXMap[GetSSCellObject(ACol, ARow).DisplayTextAlignment];
end;

function TCustomdxSpreadSheetReportLink.GetSourceCellTextAlignY(ACol, ARow: Integer): TcxTextAlignY;
begin
  if IsFixedCell(ACol, ARow) then
    Result := taBottom
  else
    Result := dxPSTextAlignYMap[GetSSCellStyle(ACol, ARow).VertTextAlign];
end;

function TCustomdxSpreadSheetReportLink.GetSourceCellTransparent(ACol, ARow: Integer): Boolean;
begin
  if not IsFixedCell(ACol, ARow) then
  begin
    Result := inherited GetSourceCellTransparent(ACol, ARow);
    if Result then 
      Result := (GetSSCellStyle(ACol, ARow).Brush.Style = fsSolid) and (GetSourceCellColor(ACol, ARow) = ColorToRGB(clWindow));
  end
  else
    Result := FixedTransparent;
end;

function TCustomdxSpreadSheetReportLink.GetSourceColWidth(ACol: Integer): Integer;
begin
  with ExposeSpreadSheetBook(CustomSpreadSheetBook) do
    if IsFixedCol(ACol) then
      Result := RowHeaderWidth
    else
      Result := ActiveSheet.Cols.Size[ACol - 1];
end;

function TCustomdxSpreadSheetReportLink.GetSourceRowHeight(ARow: Integer): Integer;
begin
  with ExposeSpreadSheetBook(CustomSpreadSheetBook) do
    if IsFixedRow(ARow) then
      Result := ColHeaderHeight
    else
      Result := ActiveSheet.Rows.Size[ARow - 1];
end;

function TCustomdxSpreadSheetReportLink.HasSelection: Boolean;
begin
  Result := inherited HasSelection or PrintAreaExists;
end;

function TCustomdxSpreadSheetReportLink.IsDrawBorder: Boolean;
begin
  Result := True;//ssovGridLines in OptionsView;
end;

function TCustomdxSpreadSheetReportLink.IsDrawHorzLines: Boolean;
begin
  Result := ssovGridLines in OptionsView;
end;

function TCustomdxSpreadSheetReportLink.IsDrawVertLines: Boolean;
begin
  Result := ssovGridLines in OptionsView;
end;

function TCustomdxSpreadSheetReportLink.IsEmptyRow(ARow: Integer): Boolean;
var
  APrevProcessingMerges: Boolean;
begin
  APrevProcessingMerges := FProcessingMerges;
  FProcessingMerges := True;
  try
    Result := inherited IsEmptyRow(ARow);
  finally
    FProcessingMerges := APrevProcessingMerges;
  end;
end;

function TCustomdxSpreadSheetReportLink.IsProcessedCol(ACol: Integer): Boolean;
begin
  Result := inherited IsProcessedCol(ACol);
  if Result then 
    if IsFixedCol(ACol) then 
      Result := IsShowRowAndColumnHeadings
    else
      Result := ExposeSpreadSheetBook(CustomSpreadSheetBook).ActiveSheet.Cols.Visible[ACol - 1];
end;

function TCustomdxSpreadSheetReportLink.IsProcessedRow(ARow: Integer): Boolean;
begin
  Result := inherited IsProcessedRow(ARow);
  if Result then 
    if IsFixedRow(ARow) then 
      Result := IsShowRowAndColumnHeadings
    else
      with ExposeSpreadSheetBook(CustomSpreadSheetBook) do      
        Result := ActiveSheet.Rows.Visible[ARow - 1];
end;

procedure TCustomdxSpreadSheetReportLink.AfterConstruct(AReportCells: TdxReportCells);
begin
  FColCount := -1;
  FRowCount := -1;
  FGridAdapter.FReportCells := nil;
  DeleteCellObjects;  
  inherited;
end;

procedure TCustomdxSpreadSheetReportLink.PrepareConstruct(AReportCells: TdxReportCells);
begin
  FExtraColumnCount := 0;
  FCellObjects.Count := ColCount * RowCount - 1;
  FGridAdapter.FReportCells := AReportCells;
  FSourceHeaderFontIndex := AddFontToPool(ExposeSpreadSheetBook(CustomSpreadSheetBook).HeaderFont);
  inherited;
end;

procedure TCustomdxSpreadSheetReportLink.UnprepareConstruct(AReportCells: TdxReportCells);
begin
  if not AbortBuilding then
  begin
    if HasMerges then AddMerges(AReportCells);
    CalcTextExtents(FGridAdapter);
    if not HasSelection then   
      AppendAdditionalColumns(FGridAdapter);
    DeleteUnneededCellSides(FGridAdapter);
    PostProcessItems(FGridAdapter);
    FixupRowWidths(FGridAdapter);
  end;
  inherited;
end;

procedure TCustomdxSpreadSheetReportLink.AddMerges(AReportCells: TdxReportCells);

  function IntersectRect(R1, R2: TRect): Boolean;
  begin
    Inc(R1.Right);
    Inc(R1.Bottom);
    Inc(R2.Right);
    Inc(R2.Bottom);
    Result := Windows.IntersectRect(R1, R1, R2);
  end;

var
  SelRect, R: TRect;
  LeftOffset, TopOffset, I, ACol, ARow: Integer;
  Overlay, Cell: TdxReportCell;
  DataClass: TdxReportCellDataClass;
  DataItem: TAbstractdxReportCellData;
begin
  FProcessingMerges := True;
  try
    SelRect := Rect(0, 0, 0, 0);
    LeftOffset := 0;
    TopOffset := 0;
    
    if HasSelection then    
    begin
      SelRect := GetSelectionRect;
      LeftOffset := GetColumnOffset(SelRect.Left);
      TopOffset := GetRowOffset(SelRect.Top);
      if IsShowRowAndColumnHeadings then 
      begin
        Dec(LeftOffset, ColumnWidths[0]);
        if not HeadersOnEveryPage then Dec(TopOffset, RowHeights[0]);
      end;
    end
    else
      if not IsShowRowAndColumnHeadings then 
      begin
        Inc(LeftOffset, ColumnWidths[0]);
        Inc(TopOffset, RowHeights[0]);
      end  
      else
        if HeadersOnEveryPage then Inc(TopOffset, RowHeights[0]);

    Overlay := AReportCells.AddOverlay;
    
    for I := 0 to MergedCells.Count - 1 do 
    begin
      R := MergedCells.MergedAsRect[I];
      OffsetRect(R, 1, 1);

      if HasSelection and not IntersectRect(R, SelRect) then 
        Continue;
          
      ACol := R.Left;
      ARow := R.Top; 
        
      R.Left := GetColumnOffset(R.Left);
      R.Top := GetRowOffset(R.Top);
      R.Right := GetColumnOffset(R.Right + 1); 
      R.Bottom := GetRowOffset(R.Bottom + 1);         

      OffsetRect(R, -LeftOffset, -TopOffset);
      
      Cell := TdxReportCell.Create(Overlay);
      Cell.BoundsRect := R;
      Cell.Transparent := True;
      Cell.CellSides := [];
      
      DataClass := GetDataItemClass(ACol);
      if DataClass <> nil then
      begin
        DataItem := DataClass.Create(Cell);
        OffsetRect(R, -R.Left, -R.Top);
        DataItem.BoundsRect := R;
        AssignData(ACol, ARow, DataItem);
        DataItem.CellSides := [];
        //DataItem.Transparent := False;//True;
      end;
    end;  
  finally
    FProcessingMerges := False;
  end;    
end;

procedure TCustomdxSpreadSheetReportLink.CheckPrintAreaBounds(var R: TRect);
begin
  if not EqualRect(R, dxPSEmptySSPrintArea) then 
    with R do 
    begin
      if Left < 0 then Left :=0;
      if Top < 0 then Top := 0;  
      if Right < Left then Right := Left;
      if Bottom < Top then Bottom := Top;
    end;  
end;

function TCustomdxSpreadSheetReportLink.GetColumnOffset(ACol: Integer): Integer;
var
  I: Integer;
begin
  Result := 0;
  for I := 0 to ACol - 1 do 
    Inc(Result, ColumnWidths[I]);
end;

function TCustomdxSpreadSheetReportLink.GetRowOffset(ARow: Integer): Integer;
var
  I: Integer;
begin
  Result := 0;
  for I := 0 to ARow - 1 do 
    Inc(Result, RowHeights[I]);
end;

function TCustomdxSpreadSheetReportLink.IsMergedBorder(ACol, ARow: Integer; 
  ASide: TdxCellSide): Boolean;
  
  function CellInRect(const R: TRect; X, Y: Integer): Boolean;
  begin
    Result := (X >= R.Left) and (X <= R.Right) and (Y >= R.Top) and (Y <= R.Bottom);
  end;
  
var
  I: Integer;
  R: TRect;
begin
  Result := IsMergedCell(ACol, ARow);
  if Result then 
    for I := 0 to MergedCells.Count - 1 do 
    begin
      R := MergedCells.MergedAsRect[I];
      OffsetRect(R, 1, 1);
      if CellInRect(R, ACol, ARow) then 
      begin
        case ASide of 
          csLeft:
            Result := ACol = R.Left;
          csTop:
            Result := ARow = R.Top;
          csRight:
            Result := ACol = R.Right;
          csBottom:
            Result := ARow = R.Bottom;
        end;
        if Result then Exit;
      end;  
      //Result := (ACol = R.Left) or (ACol = R.Right) or (ARow = R.Top) or (ARow = R.Bottom);
    end;
end;

function TCustomdxSpreadSheetReportLink.IsMergedCell(ACol, ARow: Integer): Boolean;
begin
  Result := not IsFixedCell(ACol, ARow) and ExposeStyle(GetSSCellStyle(ACol, ARow)).Merge;
end;

function TCustomdxSpreadSheetReportLink.IsNearMostLeftCell(ACol, ARow: Integer): Boolean;
var
  R: TRect;
begin
  if IsShowRowAndColumnHeadings then 
  begin
    if HasSelection then 
    begin
      R := GetSelectionRect;
      Dec(R.Left);
      Dec(ACol, R.Left);
    end;
    Result := ACol = 1
  end
  else
    Result := False;//ACol = 0;
end;

function TCustomdxSpreadSheetReportLink.IsNearMostTopCell(ACol, ARow: Integer): Boolean;
var
  R: TRect;
begin
  if IsShowRowAndColumnHeadings then 
  begin
    if HasSelection then 
    begin
      R := GetSelectionRect;
      Dec(R.Top);
      Dec(ARow, R.Top);
    end;
    Result := ARow = 1
  end  
  else
    Result := False;//ARow = 0;
end;

function TCustomdxSpreadSheetReportLink.IsShowGridLines: Boolean;
begin
  Result := ssovGridLines in OptionsView;
end;

function TCustomdxSpreadSheetReportLink.IsShowRowAndColumnHeadings: Boolean;  
begin
  Result := ssovRowAndColumnHeadings in OptionsView;
end;

function TCustomdxSpreadSheetReportLink.IsSuppressSourceFormats: Boolean;  
begin
  Result := ssovSuppressSourceFormats in OptionsView;
end;

procedure TCustomdxSpreadSheetReportLink.AppendAdditionalColumns(AnAdapter: TdxPSGridCellsAdapter);

  function DoCalcExtraColumnCount(AnAdapter: TdxPSGridCellsAdapter; 
    ADefaultColumnWidth: Integer): Integer;
  var
    Col, Row, Index, CurrentColumnOffset: Integer;
    Item: TdxReportCellSSString;
  begin
    Result := AnAdapter.ColCount;

    for Row := 0 to AnAdapter.RowCount - 1 do 
      for Col := 0 to AnAdapter.ColCount - 1 do 
      begin
        Item := AnAdapter[Col, Row];
        if Item.TextExtentRight <> 0 then 
        begin
          Index := Col;
          CurrentColumnOffset := AnAdapter.ColOffsets[Index];
          while Item.TextExtentRight > CurrentColumnOffset do  
          begin
            Inc(Index);
            if Index <= AnAdapter.ColCount then 
              CurrentColumnOffset := AnAdapter.ColOffsets[Index]
            else
              Inc(CurrentColumnOffset, ADefaultColumnWidth);
          end;
          if Index > Result then Result := Index;
        end;
      end;  
    Dec(Result, AnAdapter.ColCount);
  end;

  function FindNearestVisibleColIndex(AStartIndex: Integer): Integer;
  begin
    Result := AStartIndex;
    with ExposeSpreadSheetBook(CustomSpreadSheetBook).ActiveSheet.Cols do
      while not Visible[Result - 1] do 
        Inc(Result);
  end;
  
  procedure DoAppendExtraColumns(AnAdapter: TdxPSGridCellsAdapter; 
    AExtraColumnCount, ADefaultColumnWidth: Integer);
  var
    L, Row, Col, StartRealCol, RealCol: Integer;
    Item: TdxReportCellSSString;
    Cell: TdxReportCell;
    R: TRect;
  begin
    L := AnAdapter.ColOffsets[AnAdapter.ColCount];
    StartRealCol := AnAdapter.Cells[AnAdapter.ColCount - 1, 0].RealCol;
    
    for Row := 0 to AnAdapter.RowCount - 1 do
    begin
      Item := nil;
      Cell := AnAdapter.Rows[Row];
      R := Bounds(L, 0, 0, Cell.Height);
      RealCol := StartRealCol;
      for Col := 0 to AExtraColumnCount - 1 do
      begin
        R.Left := R.Right;
        R.Right := R.Left + ADefaultColumnWidth;
          
        Item := TdxReportCellSSString.Create(Cell);
        Item.BoundsRect := R;
        Item.IsVirtual := True;
        
        RealCol := FindNearestVisibleColIndex(RealCol + 1);
        AssignData(RealCol, AnAdapter.RowIndexes[Row], Item);
        if Item.IsFixed then 
          Item.Text := TcxSSUtils.ColumnNameByIndex(RealCol - 1, CustomSpreadSheetBook.R1C1ReferenceStyle);
      end;
      Cell.Width := Item.BoundsRect.Right;
    end;
  end;

var
  DefaultColumnWidth: Integer;
begin
  FAppendingExtraColumns := True;
  try
    DefaultColumnWidth := ExposeSpreadSheetBook(CustomSpreadSheetBook).DefaultColWidth;
    FExtraColumnCount := DoCalcExtraColumnCount(FGridAdapter, DefaultColumnWidth);
    if ExtraColumnCount <> 0 then 
      DoAppendExtraColumns(FGridAdapter, ExtraColumnCount, DefaultColumnWidth);
  finally
    FAppendingExtraColumns := False;
  end;    
end;

procedure TCustomdxSpreadSheetReportLink.PostProcessItems(AnAdapter: TdxPSGridCellsAdapter);
var
  Col, Row: Integer;
begin
  for Col := 0 to AnAdapter.ColCount - 1 do
    for Row := 0 to AnAdapter.RowCount - 1 do
      with AnAdapter.Cells[Col, Row] do 
        if not IsFixed and not IsVirtual then 
        begin
          CalcBorderSubs(AnAdapter);
          CalcDoubleBorderSlants(AnAdapter);
        end;  
end;

procedure TCustomdxSpreadSheetReportLink.CalcTextExtents(AnAdapter: TdxPSGridCellsAdapter);

  function NeedCalcItemTextExtents(AItem: TdxReportCellSSString): Boolean;
  begin
    with AItem do
      Result := not IsMerged and not IsFixed and not Multiline and not Fill and 
        (Text <> '') and (TextAlignX in [taLeft, taCenterX, taRight]);
  end;

  procedure DoCalcItemTextExtents(AItem: TdxReportCellSSString; Col, Row: Integer);

    function CalcTextRightExtent(ATextWidth, ACol, ARow: Integer): Integer;
    var
      OriginalColumnOffset, CurrentColumnOffset, EndOfTextOffset, I: Integer;  
      Item: TdxReportCellSSString;
    begin
      OriginalColumnOffset := AnAdapter.ColOffsets[ACol];
      EndOfTextOffset := OriginalColumnOffset + ATextWidth;      
      Result := 0;
    
      CurrentColumnOffset := AnAdapter.ColOffsets[ACol + 1];
      
      for I := ACol + 1 to AnAdapter.ColCount do 
      begin
        CurrentColumnOffset := AnAdapter.ColOffsets[I];
        if CurrentColumnOffset > EndOfTextOffset then       
          Break;
          
        if I < AnAdapter.ColCount then 
          Item := AnAdapter[I, ARow]
        else
          Item := nil;  
          
        if (Item <> nil) and (Item.IsMerged or (Item.Text <> '')) then 
        begin
          Result := CurrentColumnOffset;
          Break;
        end  
      end;
      
      if Result = 0 then 
      begin
        Result := EndOfTextOffset;
        if (Result > CurrentColumnOffset) and HasSelection then        
          Result := CurrentColumnOffset + dxTextSpace;
        if Result < AnAdapter.ColOffsets[ACol + 1] then Result := 0;
      end;  
    end;

    function CalcTextLeftExtent(ATextWidth, ACol, ARow: Integer): Integer;
    var
      TextLeftEdge, I, CurrentColumnOffset: Integer;
      Item: TdxReportCellSSString;
    begin
      TextLeftEdge := AnAdapter.ColOffsets[ACol + 1] - ATextWidth;
      if TextLeftEdge < 0 then TextLeftEdge := 0;
      
      Result := 0;

      for I := ACol downto 0 do
      begin
        if I > 0 then 
          Item := AnAdapter[I - 1, ARow]
        else
          Item := nil;
        CurrentColumnOffset := AnAdapter.ColOffsets[I];
        if CurrentColumnOffset < TextLeftEdge then       
          Break;
        if (Item = nil) or Item.IsFixed or Item.IsMerged or (Item.Text <> '') then 
        begin
          Result := CurrentColumnOffset;
          Break;
        end  
      end; 
      
      if Result = 0 then 
      begin 
        Result := TextLeftEdge;
        if Result > AnAdapter.ColOffsets[ACol] then Result := 0;
      end;  
    end;

  var
    TextWidth: Integer;
  begin
    TextWidth := GetTextWidth(ScreenCanvas, AItem.Font, AItem.Text);
  
    case AItem.TextAlignX of
      taCenterX:
        begin
          Dec(TextWidth, (TextWidth - AItem.Width) div 2);
          AItem.TextExtentRight := CalcTextRightExtent(TextWidth + 1 * dxTextSpace, Col, Row);
          AItem.TextExtentLeft := CalcTextLeftExtent(TextWidth + 1 * dxTextSpace, Col, Row);
        end;
      taLeft:
        AItem.TextExtentRight := CalcTextRightExtent(TextWidth + 3 * dxTextSpace, Col, Row);
      taRight:                          
        AItem.TextExtentLeft := CalcTextLeftExtent(TextWidth + 3 * dxTextSpace, Col, Row);
    end;               
  end;
  
var
  Col, Row: Integer;
  Item: TdxReportCellSSString;
begin
  ScreenCanvas.SaveState;
  try
    for Col := 0 to AnAdapter.ColCount - 1 do
      for Row := 0 to AnAdapter.RowCount - 1 do
      begin
        Item := AnAdapter[Col, Row];
        if NeedCalcItemTextExtents(Item) then
          DoCalcItemTextExtents(Item, Col, Row);
      end;
  finally
    ScreenCanvas.RestoreState;
  end;
end;

procedure TCustomdxSpreadSheetReportLink.DeleteCellObjects;
var
  I: Integer;
begin                  
  for I := 0 to FCellObjects.Count - 1 do 
    TObject(FCellObjects[I]).Free;
  FCellObjects.Clear;
end;
      
procedure TCustomdxSpreadSheetReportLink.DeleteUnneededCellSides(AnAdapter: TdxPSGridCellsAdapter);
  
  procedure DoDeleteUnneededCellSidesFromRightSide(AItem: TdxReportCellSSString; ACol: Integer);
  var
    TextRightExtent, Col, CurrentColumnOffset: Integer;
  begin
    TextRightExtent := AItem.TextExtentRight;
    for Col := ACol + 1 to AnAdapter.ColCount - 1 do
    begin
      CurrentColumnOffset := AnAdapter.ColOffsets[Col];
      if CurrentColumnOffset < TextRightExtent then 
      begin
        AItem.CellSides := AItem.CellSides - [csRight];
        AItem := TdxReportCellSSString(AItem.getNextSibling);
        if AItem = nil then
          Break;
        AItem.CellSides := AItem.CellSides - [csLeft];
      end;  
    end;
  end;

  procedure DoDeleteUnneededCellSidesFromLeftSide(AItem: TdxReportCellSSString; ACol: Integer);
  var
    TextLeftExtent, Col, CurrentColumnOffset: Integer;
  begin
    TextLeftExtent := AItem.TextExtentLeft;
    for Col := ACol downto 0 do
    begin
      CurrentColumnOffset := AnAdapter.ColOffsets[Col];
      if CurrentColumnOffset > TextLeftExtent then 
      begin
        AItem.CellSides := AItem.CellSides - [csLeft];
        AItem := TdxReportCellSSString(AItem.getPrevSibling);
        if (AItem = nil) or AItem.IsFixed then 
          Break;
        AItem.CellSides := AItem.CellSides - [csRight];
      end;  
    end;
  end;

var
  Col, Row: Integer;
  Item: TdxReportCellSSString;
begin
  for Row := 0 to AnAdapter.RowCount - 1 do
    for Col := 0 to AnAdapter.ColCount - 1 do
    begin
      Item := AnAdapter[Col, Row];
      if Item.TextExtentRight <> 0 then 
        DoDeleteUnneededCellSidesFromRightSide(Item, Col);
      if Item.TextExtentLeft <> 0 then 
        DoDeleteUnneededCellSidesFromLeftSide(Item, Col);
    end;
end;

procedure TCustomdxSpreadSheetReportLink.FixupRowWidths(AnAdapter: TdxPSGridCellsAdapter);
var
  MaxWidth, I: Integer;
begin
  with AnAdapter do 
  begin
    MaxWidth := 0;
    for I := 0 to ColCount - 1 do 
      Inc(MaxWidth, ColWidths[I]);
      
    for I := 0 to RowCount - 1 do 
      Rows[I].Width := MaxWidth;
  end;  
end;

function TCustomdxSpreadSheetReportLink.HasMerges: Boolean;
begin
  Result := MergedCells.Count <> 0;
end;

function TCustomdxSpreadSheetReportLink.IsEmptyCell(const ACell: TcxSSCellRec): Boolean;
var
  DefaultStyle: TcxSSCellStyleRec;
begin
  DefaultStyle := ExposeDataStorage(GetSSDataStorage).DefaultCellRec.StylePtr^;
  Result := IsCellDataEmpty(ACell) and AreStylesEqual(DefaultStyle, ACell.StylePtr^);    
end;

function TCustomdxSpreadSheetReportLink.OnlyEdgeIsAssigned(const ACell: TcxSSCellRec; 
  AEdge: TcxSSEdgeBorder): Boolean;
var
  DefaultStyle: TcxSSCellStyleRec;
begin
  DefaultStyle := ExposeDataStorage(GetSSDataStorage).DefaultCellRec.StylePtr^;
  Result := IsCellDataEmpty(ACell) and AreStylesEqual(DefaultStyle, ACell.StylePtr^, [AEdge]);
end;

function TCustomdxSpreadSheetReportLink.OnlyLeftEdgeIsAssigned(ACell: TcxSSCellRec): Boolean;
begin
  Result := OnlyEdgeIsAssigned(ACell, eLeft);
end;

function TCustomdxSpreadSheetReportLink.OnlyTopEdgeIsAssigned(ACell: TcxSSCellRec): Boolean;
begin
  Result := OnlyEdgeIsAssigned(ACell, eTop);
end;

procedure TCustomdxSpreadSheetReportLink.SetupCellBorders(ACol, ARow: Integer;
  ADataItem: TAbstractdxReportCellData);
var
  DefaultAutoColor, DefaultColor: TColor;
  Style: TcxSSCellStyle;
  Side: TdxCellSide;
  Border: TdxPSCellBorder;
begin
  if not IsFixedCell(ACol, ARow) then 
  begin
    DefaultAutoColor := ColorToRGB(clWindowText);
    DefaultColor := ColorToRGB(ExposeSpreadSheetBook(CustomSpreadSheetBook).GridColor);
        
    if IsSuppressSourceFormats then
      Style := nil
    else  
      Style := GetSSCellStyle(ACol, ARow);

    for Side := csLeft to csBottom do
      if Side in ADataItem.CellSides then
      begin
        if Style <> nil then
        begin
          with Style.Borders.Edges[cxSSEdgeBorderMap[Side]] do 
          begin
            Border.Pattern := dxPSEdgePatternClassMap[Style];
            if Style = lsDefault then 
              Border.Color := GetRealColor(Color, DefaultColor)
            else  
              Border.Color := GetRealColor(Color, DefaultAutoColor);            
          end;
          if IsSSDefaultStyle(Style) then 
            Border.Color := GetRealColor(GetSSDefaultCellSideColor(Side), DefaultColor);
        end
        else
          if not IsMergedCell(ACol, ARow) then 
          begin
            Border.Color := GetRealColor(GetSSDefaultCellSideColor(Side), ColorToRGB(GridLineColor));
            Border.Pattern := TdxPSSolidEdgePattern; 
          end
          else
            Border.Pattern := nil;

        TdxReportCellSSString(ADataItem).Borders[Side] := Border;
      end;
  end
end;
 
function TCustomdxSpreadSheetReportLink.GetBookSheet: TcxSSBookSheet;
begin
  if CustomSpreadSheetBook = nil then 
    Result := nil
  else
    Result := ExposeSpreadSheetBook(CustomSpreadSheetBook).ActiveSheet;
end;

function TCustomdxSpreadSheetReportLink.GetCustomSpreadSheetBook: TcxCustomSpreadSheetBook;
begin
  Result := TcxCustomSpreadSheetBook(Component);
end;

function TCustomdxSpreadSheetReportLink.GetMeaningColCount: Integer;
var
  Storage: TcxSSDataStorageAccess;
  Col, Row, CandidateCol: Integer;
  Cell: TcxSSCellRec;
  R: TRect;
begin
  Result := -1;
  CandidateCol := 0;
  Storage := ExposeDataStorage(GetSSDataStorage);
  for Row := 0 to Storage.Capacity.cY - 1 do 
  begin
    for Col := Storage.Capacity.cX - 1 downto 0 do
    begin
      Cell := Storage.Cells[Col, Row];
      if not IsEmptyCell(Cell) then 
      begin
        CandidateCol := Col;
        if (CandidateCol > 0) and OnlyLeftEdgeIsAssigned(Cell) then Dec(CandidateCol);
        Break;
      end;
    end;
    if CandidateCol > Result then Result := CandidateCol;
  end;  

  for Col := 0 to MergedCells.Count - 1 do 
  begin
    R := Storage.MergedCells.MergedAsRect[Col];
    if R.Right > Result then Result := R.Right;
  end;
  
  if Result > -1 then Inc(Result);
end;

function TCustomdxSpreadSheetReportLink.GetMeaningRowCount: Integer;    
var
  Storage: TcxSSDataStorageAccess;
  CandidateRow, Col, Row: Integer;
  Cell: TcxSSCellRec;
  IsBreaked: Boolean;
  R: TRect;
begin
  Storage := ExposeDataStorage(GetSSDataStorage);
  CandidateRow := -1;
  
  for Row := Storage.Capacity.cY - 1 downto 0 do 
  begin
    IsBreaked := False;
    for Col := 0 to Storage.Capacity.cX - 1 do
    begin
      Cell := Storage.Cells[Col, Row];
      if not IsEmptyCell(Cell) then 
      begin
        CandidateRow := Row;
        if (CandidateRow = 0) or not OnlyTopEdgeIsAssigned(Cell) then 
        begin
          IsBreaked := True;
          Break;
        end;
      end;
    end;
    if CandidateRow <> -1 then 
    begin 
      if not IsBreaked then Dec(CandidateRow);
      Break;
    end;  
  end;  
  Result := CandidateRow;
  
  for Col := 0 to Storage.MergedCells.Count - 1 do 
  begin
    R := Storage.MergedCells.MergedAsRect[Col];
    if R.Bottom > Result then Result := R.Bottom;
  end;
  Result := Min(Result, Storage.MaxRow + 1);
  if Result > -1 then Inc(Result);
end;

function TCustomdxSpreadSheetReportLink.GetMergedCells: TcxSSMergedCellsStorage;
begin
  Result := ExposeDataStorage(ExposeBookSheet(BookSheet).DataStorage).MergedCells;
end;

procedure TCustomdxSpreadSheetReportLink.SetOptionsView(Value: TdxPSSpreadSheetReportLinkOptionsView);
begin
  if FOptionsView <> Value then
  begin
    FOptionsView := Value;
    if ssovSuppressSourceFormats in FOptionsView then 
      DrawMode := gdmStrict
    else  
      DrawMode := gdmBorrowSource;
    LinkModified(True);
  end;
end;

procedure TCustomdxSpreadSheetReportLink.SetPrintArea(Value: TRect);
begin
  CheckPrintAreaBounds(Value);
  if not EqualRect(Value, FPrintArea) then
  begin
    FPrintArea := Value;
    LinkModified(True);
  end;                             
end;

function TCustomdxSpreadSheetReportLink.GetSSCellObject(ACol, ARow: Integer): TcxSSCellObject;
var
  Index: Integer;
begin
  Index := GetFlatIndex(ACol - 1, ARow - 1);
  if Index > FCellObjects.Count - 1 then // ExtraColumns
    Result := nil
  else
    Result := FCellObjects.List[Index];
    
  if Result = nil then
  begin                                                     
    Result := ExposeActiveSpreadSheet(CustomSpreadSheetBook).GetCellObject(ACol - 1, ARow - 1);
    if Index > FCellObjects.Count - 1 then 
      FCellObjects.Count := Index + 1;
    FCellObjects.List[Index] := Result;
  end;
end;

function TCustomdxSpreadSheetReportLink.GetSSCellStyle(ACol, ARow: Integer): TcxSSCellStyle;
var
  CellObject: TcxSSCellObject;
begin
  if not FAppendingExtraColumns then
  begin
    CellObject := GetSSCellObject(ACol, ARow);
    if (CellObject <> nil) and CellObject.StyleExist then 
      Result := CellObject.Style
    else
      Result := GetSSDefaultStyle;
  end    
  else
    Result := GetSSDefaultStyle;
end;

function TCustomdxSpreadSheetReportLink.GetSSColorPalette: PcxExcelPalette;
begin
  Result := ExposeSpreadSheetBook(CustomSpreadSheetBook).Palette;
end;

function TCustomdxSpreadSheetReportLink.GetSSDataStorage: TcxSSDataStorage;
begin
  Result := ExposeBookSheet(BookSheet).DataStorage;
end;

function TCustomdxSpreadSheetReportLink.GetSSDefaultCellSideColor(ASide: TdxCellSide): TColor;
begin
  with ExposeSpreadSheetBook(CustomSpreadSheetBook), DefaultStyle.Borders do 
    if ASide in csLeftRight then
      if VerticalBorders.Style <> lsDefault then
        Result := VerticalBorders.Color
      else
        Result := ColorToRGB(GridColor)
    else
      if HorizontalBorders.Style <> lsDefault then
        Result := HorizontalBorders.Color
      else
        Result := ColorToRGB(GridColor);
end;

function TCustomdxSpreadSheetReportLink.GetSSDefaultStyle: TcxSSCellStyle;
begin
  Result := ExposeSpreadSheetBook(CustomSpreadSheetBook).StyleCache.DefaultStyle;
end;
  
function TCustomdxSpreadSheetReportLink.GetFlatIndex(ACol, ARow: Integer): Integer;
begin
  Result := ARow * (ColCount - 1) + ACol;
end;

function TCustomdxSpreadSheetReportLink.GetRealColor(AColorIndex: Integer; ADefaultColor: TColor): TColor;
begin
  if AColorIndex > SystemColorStart then
    Result := ADefaultColor
  else
    Result := GetSSColorPalette^[AColorIndex];
  Result := ColorToRGB(Result);
end;

function TCustomdxSpreadSheetReportLink.IsSSDefaultStyle(AStyle: TcxSSCellStyle): Boolean;
begin
  Result := AStyle = GetSSDefaultStyle;
end;

{ TdxSpreadSheetBookReportLink }

function TdxSpreadSheetBookReportLink.GetSpreadSheetBook: TcxSpreadSheetBook;
begin
  Result := TcxSpreadSheetBook(Component);
end;

{ TdxSpreadSheetReportLink }

function TdxSpreadSheetReportLink.GetSpreadSheet: TcxSpreadSheet;
begin
  Result := TcxSpreadSheet(Component);
end;

{ TfmdxSpreadSheetDesignWindow }

constructor TfmdxSpreadSheetDesignWindow.Create(AOwner: TComponent);
begin
  HelpContext := dxhccxSpreadSheetReportLinkDesigner;
  inherited;
  CreateControls;
  pgctrlMain.ActivePage := pgctrlMain.Pages[0];
end;

procedure TfmdxSpreadSheetDesignWindow.DoInitialize;
begin
  inherited;
  chbxShowRowAndColumnHeadings.Checked := ssovRowAndColumnHeadings in ReportLink.OptionsView;
  chbxShowGridLines.Checked := ssovGridLines in ReportLink.OptionsView;

//  chbxAutoWidth.Checked := ReportLink.AutoWidth;
  chbxRowAutoHeight.Checked := ReportLink.RowAutoHeight;
  chbxSuppressSourceFormats.Checked := ssovSuppressSourceFormats in ReportLink.OptionsView;
  
  chbxTransparent.Checked := ReportLink.Transparent;
  ccbxColor.ColorValue := ReportLink.Color;
  chbxFixedTransparent.Checked := ReportLink.FixedTransparent;
  ccbxFixedColor.ColorValue := ReportLink.FixedColor;
  ccbxLineColor.ColorValue := ReportLink.GridLineColor;

  FontInfoToText(ReportLink.Font, edFont);
  FontInfoToText(ReportLink.FixedFont, edFixedFont);          
  
  chbxFixedRowsOnEveryPage.Checked := ReportLink.HeadersOnEveryPage;
  chbxOnlySelected.Checked := ReportLink.OnlySelected;
end;

function TfmdxSpreadSheetDesignWindow.GetPreviewHost: TCustomPanel;
begin
  Result := pnlPreview;
end;

procedure TfmdxSpreadSheetDesignWindow.LoadGroupsIcons;
begin
  inherited LoadGroupsIcons;
  dxLoadIconFromResourceEx(imgGrid, IDB_DXPSGROUPICON_SHOW);
  dxLoadIconFromResourceEx(Image5, IDB_DXPSGROUPICON_FRAMING);
  dxLoadIconFromResourceEx(Image1, IDB_DXPSGROUPICON_ONEVERYPAGE);
  dxLoadIconFromResourceEx(Image3, IDB_DXPSGROUPICON_SELECTION);
end;

procedure TfmdxSpreadSheetDesignWindow.LoadStrings;
begin
  inherited;

  tshOptions.Caption := cxGetResourceString(@sdxOptions);
  tshFont.Caption := cxGetResourceString(@sdxFonts);
  tshColor.Caption := cxGetResourceString(@sdxColors);
  tshBehaviors.Caption := cxGetResourceString(@sdxBehaviors);
  lblPreview.Caption := DropAmpersand(cxGetResourceString(@sdxPreview));

  lblShow.Caption := cxGetResourceString(@sdxShow);
  chbxShowRowAndColumnHeadings.Caption := cxGetResourceString(@sdxShowRowAndColumnHeadings);
  chbxShowGridLines.Caption := cxGetResourceString(@sdxShowGridLines);
  
  lblMiscellaneous.Caption := cxGetResourceString(@sdxMiscellaneous);
  //chbxAutoWidth.Caption := cxGetResourceString(@sdxAutoWidth);
  chbxRowAutoHeight.Caption := cxGetResourceString(@sdxRowAutoHeight);
  chbxSuppressSourceFormats.Caption := cxGetResourceString(@sdxSuppressSourceFormats);

  stTransparent.Caption := ' ' + cxGetResourceString(@sdxTransparent) + ' ';
  lblColor.Caption := cxGetResourceString(@sdxColor);
  stFixedTransparent.Caption := ' ' + cxGetResourceString(@sdxFixedTransparent) + ' ';
  lblFixedColor.Caption := cxGetResourceString(@sdxFixedColor);
  lblGridLinesColor.Caption := cxGetResourceString(@sdxGridLinesColor);

  btnFont.Caption := cxGetResourceString(@sdxBtnFont);
  btnFixedFont.Caption := cxGetResourceString(@sdxBtnFixedFont);

  lblOnEveryPage.Caption := cxGetResourceString(@sdxOnEveryPage);
  chbxFixedRowsOnEveryPage.Caption := cxGetResourceString(@sdxRepeatHeaderRowAtTop);

  lblSelection.Caption := cxGetResourceString(@sdxSelection);
  chbxOnlySelected.Caption := cxGetResourceString(@sdxOnlySelected);
end;

procedure TfmdxSpreadSheetDesignWindow.PaintPreview(ACanvas: TCanvas; R: TRect);
begin
  inherited;
  dxPSBaseGridLnk.dxPSDrawGridPreview(ACanvas, R, ReportLink, 
    ReportLink.IsShowRowAndColumnHeadings, ReportLink.IsShowRowAndColumnHeadings);
end;

procedure TfmdxSpreadSheetDesignWindow.UpdateControlsState;
begin
  inherited;
  ccbxColor.Enabled := not chbxTransparent.Checked;
  lblColor.Enabled := ccbxColor.Enabled;
  ccbxFixedColor.Enabled := not chbxFixedTransparent.Checked;
  lblFixedColor.Enabled := ccbxFixedColor.Enabled;
  chbxFixedRowsOnEveryPage.Enabled := not ReportLink.IsAggregated;
end;

procedure TfmdxSpreadSheetDesignWindow.UpdatePreview;
begin
  FPreviewBox.Invalidate;
end;

function TfmdxSpreadSheetDesignWindow.GetReportLink: TCustomdxSpreadSheetReportLink;
begin
  Result := inherited ReportLink as TCustomdxSpreadSheetReportLink;
end;

procedure TfmdxSpreadSheetDesignWindow.SetReportLink(Value: TCustomdxSpreadSheetReportLink);
begin
  inherited ReportLink := Value;
end;

procedure TfmdxSpreadSheetDesignWindow.CreateControls;
begin
  FPreviewBox := TdxPSPaintPanel.Create(pnlPreview);
  with TdxPSPaintPanel(FPreviewBox) do
  begin
    Parent := pnlPreview;
    Align := alClient;
    EdgeInner := esNone;
    EdgeOuter := esNone;
    OnPaint := pbxPreviewPaint;
  end;
end;

procedure TfmdxSpreadSheetDesignWindow.CMDialogChar(var Message: TCMDialogChar);
var
  I: Integer;
begin
  inherited;
  with pgctrlMain do
    for I := 0 to PageCount - 1 do
      if IsAccel(Message.CharCode, Pages[I].Caption) then
      begin
        Message.Result := 1;
        ActivePage := Pages[I];
        Exit;
      end;
end;

procedure TfmdxSpreadSheetDesignWindow.pbxPreviewPaint(Sender: TObject);
begin
  with TdxPSPaintPanel(Sender) do
    PaintPreview(Canvas, ClientRect);
end;

procedure TfmdxSpreadSheetDesignWindow.chbxFixedRowsOnEveryPageClick(Sender: TObject);                              
begin
  if LockControlsUpdate then Exit;
  ReportLink.HeadersOnEveryPage := TcxCheckBox(Sender).Checked;
  Modified := True;
end;

procedure TfmdxSpreadSheetDesignWindow.chbxOnlySelectedClick(Sender: TObject);
begin
  if LockControlsUpdate then Exit;
  ReportLink.OnlySelected := TcxCheckBox(Sender).checked;
  Modified := True;
end;

procedure TfmdxSpreadSheetDesignWindow.ccbxColorChange(Sender: TObject);
var
  Color: TColor;
begin
  if LockControlsUpdate then Exit;
  Color := TcxColorComboBox(Sender).ColorValue;
  case TcxColorComboBox(Sender).Tag of
    0: ReportLink.Color := Color;
    1: ReportLink.FixedColor := Color;
    2: ReportLink.GridLineColor := Color;
  end;
  Modified := True;
  UpdatePreview;
end;

procedure TfmdxSpreadSheetDesignWindow.btnFixedFontClick(Sender: TObject);

  function GetEdit: TcxTextEdit;
  begin
    if TcxButton(Sender).Tag = 0 then
      Result := edFont
    else
      Result := edFixedFont;  
  end;
  
begin
  if LockControlsUpdate then Exit;
  
  with dxPSGlbl.FontDialog do 
  begin
    case TcxButton(Sender).Tag of
      0: Font := ReportLink.Font;
      1: Font := ReportLink.FixedFont;
    end;
  
    if Execute then
    begin
      case TcxButton(Sender).Tag of
        0: ReportLink.Font := Font;
        1: ReportLink.FixedFont := Font;
      end;
      FontInfoToText(Font, GetEdit);
      Modified := True;
      UpdatePreview;
    end;
  end;  
end;

procedure TfmdxSpreadSheetDesignWindow.chbxRowAutoHeightClick(
  Sender: TObject);
begin
  if LockControlsUpdate then Exit;
  ReportLink.RowAutoHeight := TcxCheckBox(Sender).Checked;
  Modified := True;
end;

procedure TfmdxSpreadSheetDesignWindow.chbxOptionsViewChanged(Sender: TObject);
var
  AOption: TdxPSSpreadSheetReportLinkOptionView;
begin
  if not LockControlsUpdate then
  begin
    case TcxCheckBox(Sender).Tag of
      0: AOption := ssovRowAndColumnHeadings;
      1: AOption := ssovGridLines;
      2: AOption := ssovSuppressSourceFormats;
      else
        Exit;
    end;

    if TcxCheckBox(Sender).Checked then
      ReportLink.OptionsView := ReportLink.OptionsView + [AOption]
    else
      ReportLink.OptionsView := ReportLink.OptionsView - [AOption];

    Modified := True;
    UpdatePreview;
  end;
end;

procedure TfmdxSpreadSheetDesignWindow.chbxFixedTransparentClick(
  Sender: TObject);
begin
  if not LockControlsUpdate then
  begin
    case TcxCheckBox(Sender).Tag of
      0: ReportLink.Transparent := TcxCheckBox(Sender).checked;
      1: ReportLink.FixedTransparent := TcxCheckBox(Sender).checked;
    end;
    Modified := True;
    UpdatePreview;
  end;
end;

procedure TfmdxSpreadSheetDesignWindow.stTransparentClick(Sender: TObject);
begin
  if chbxTransparent.CanFocus then
    ActiveControl := chbxTransparent;
  chbxTransparent.Checked := not chbxTransparent.Checked;
end;

procedure TfmdxSpreadSheetDesignWindow.stFixedTransparentClick(Sender: TObject);
begin
  if chbxFixedTransparent.CanFocus then
    ActiveControl := chbxFixedTransparent;
  chbxFixedTransparent.Checked := not chbxFixedTransparent.Checked;
end;

procedure TfmdxSpreadSheetDesignWindow.lblColorClick(Sender: TObject);
begin
  ActivateComboBoxControl(Self, TcxLabel(Sender).FocusControl);
end;

procedure TfmdxSpreadSheetDesignWindow.pgctrlMainChange(Sender: TObject);
begin
  lblPreview.Parent := TcxPageControl(Sender).ActivePage;
end;

procedure RegisterItems;
begin
  TdxReportCellSSString.Register;
end;

procedure UnregisterItems;
begin
  TdxReportCellSSString.Unregister;
end;

procedure RegisterAssistants;
begin
  TdxPSSSStringGridCellDataMap.Register;
end;

procedure UnregisterAssistants;
begin
  TdxPSSSStringGridCellDataMap.Unregister;
end;

initialization
  RegisterAssistants;
  RegisterItems;
  
  dxPSRegisterReportLink(TdxSpreadSheetBookReportLink, TcxSpreadSheetBook, TfmdxSpreadSheetDesignWindow);
  dxPSRegisterReportLink(TdxSpreadSheetReportLink, TcxSpreadSheet, TfmdxSpreadSheetDesignWindow);
  
finalization
  dxPSUnregisterReportLink(TdxSpreadSheetReportLink, TcxSpreadSheet, TfmdxSpreadSheetDesignWindow);
  dxPSUnregisterReportLink(TdxSpreadSheetBookReportLink, TcxSpreadSheetBook, TfmdxSpreadSheetDesignWindow);

  UnregisterItems;
  UnregisterAssistants;
  
end.
 
